Advanced Graphics and Data Visualization in R

Lecture 04: Expressing expression data


0.1.0 An overview of Advanced Graphics and Data Visualization in R

“Advanced Graphics and Data Visualization in R” is brought to you by the Centre for the Analysis of Genome Evolution & Function’s (CAGEF) bioinformatics training initiative. This CSB1021 was developed to enhance the skills of students with basic backgrounds in R by focusing on available philosophies, methods, and packages for plotting scientific data. While the datasets and examples used in this course will be centred on SARS-CoV-2 epidemiological and genomic data, the lessons learned herein will be broadly applicable.

This lesson is the fourth in a 6-part series. The aim for the end of this series is for students to recognize how to import, format, and display data based on their intended message and audience. The format and style of these visualizations will help to identify and convey the key message(s) from their experimental data.

The structure of the class is a code-along style in R markdown notebooks. At the start of each lecture, skeleton versions of the lecture will be provided for use on the University of Toronto datatools Hub so students can program along with the instructor.


0.2.0 Lecture objectives

This week will focus on standard visualizations of expression data. While our prior topics have focused on making visualizations that are applicable broadly to many types of data, a number of this week’s visualizations are used mainly on multi-dimensional data from experiments like RNAseq.

At the end of this lecture you will have covered the following topics

  1. Visualizing flowcharts and workflows
  2. Scatterplot variants of RNAseq data
  3. Colour gradient visualizations aka heatmaps
  4. RNAseq data analysis with goseq
  5. Visualizing relationships between samples

0.3.0 A legend for text format in R markdown

grey background - a package, function, code, command or directory. Backticks are also use for in-line code.
italics - an important term or concept or an individual file or folder
bold - heading or a term that is being defined
blue text - named or unnamed hyperlink

... - Within each coding cell this will indicate an area of code that students will need to complete for the code cell to run correctly.

Blue box: A key concept that is being introduced

Yellow box: Risk or caution

Green boxes: Recommended reads and resources to learn Python

Red boxes: A comprehension question which may or may not involve a coding cell. You usually find these at the end of a section.


0.4.0 Lecture and data files used in this course

0.4.1 Weekly Lecture and skeleton files

Each week, new lesson files will appear within your RStudio folders. We are pulling from a GitHub repository using this Repository git-pull link. Simply click on the link and it will take you to the University of Toronto datatools Hub. You will need to use your UTORid credentials to complete the login process. From there you will find each week’s lecture files in the directory /2024-03-Adv_Graphics_R/Lecture_XX. You will find a partially coded skeleton.Rmd file as well as all of the data files necessary to run the week’s lecture.

Alternatively, you can download the R-Markdown Notebook (.Rmd) and data files from the RStudio server to your personal computer if you would like to run independently of the Toronto tools.

0.4.2 Live-coding HTML page

A live lecture version will be available at camok.github.io that will update as the lecture progresses. Be sure to refresh to take a look if you get lost!

0.4.3 Post-lecture PDFs

As mentioned above, at the end of each lecture there will be a completed version of the lecture code released as a PDF file under the Modules section of Quercus.

0.4.4 Data used in this lesson

Today’s datasets will focus on differential expression analysis. The basic visualizations of this data after it has already been processed by packages like DESeq2.

0.4.4.1 Dataset 1: Lecture04_sankey_data.csv

This is an example dataset used for building a Sankey diagram. It builds a theoretical workflow for RNAseq analysis and visualization within a manuscript. What is a Sankey diagram? We’ll find out!

0.4.4.2 Dataset 2: Wyler2020_AEC_SARSCoV2_17AAG_readcounts.tsv

RNA-Seq read count data generated from SARS-CoV2 infections of AEC cells. Used to compare the timecourse of expression (pro-inflammatory) changes in samples treated with and without HSP90 inhibitors. Published in iScience doi: https://doi.org/10.1016/j.isci.2021.102151

0.4.4.3 Dataset 3: Blanco-Melo2020Cell.Supp1.xlsx

This is an RNAseq dataset comparison for infection of multiple cell types with pathogens like SARS-CoV-2, SARS-CoV-1, MERS-CoV, RSV (respiratory syncytial virus), IAV (influenza A virus) and HPIV3 (human parainfluenze virus type 3) published in Cell doi: 10.1016/j.cell.2020.04.026

0.4.4.4 Dataset 4: Blanco-Melo2020_Supp_Data_4.xlsx

This is an RNAseq dataset comparison for infection of human alveolar adenocarcinoma (A549) cells with and without influenza A virus (IAV) published on bioRxiv doi: https://doi.org/10.1101/2020.03.24.004655

0.4.4.5 Dataset 5: Lecture04.RData

A saved file with a final GO annotation dataset we look at with our visualizations of dot plots.


0.5.0 Packages used in this lesson

tidyverse which has a number of packages including dplyr, tidyr, stringr, forcats and ggplot2

viridis helps to create color-blind palettes for our data visualizations

networkD3, gghighlight, ggrepel, and UpSetR are used in building some of our visualizations including flowcharts, and upset plots.

goseq, and org.Hs.eg.db are packages that will help us perform some basic Gene Ontology (GO) annotations with our data.

# # Install these packages onto r.datatools
# install.packages("devtools")
# install.packages("networkD3")
# install.packages("gghighlight")
# install.packages("ComplexUpset", type="source")
# install.packages("GGally")
# install.packages("ggrepel")
# 
# 
# # 220331: Current version of JupyterHub is not allowing goseq to be installed correctly
# 
# # # Some packages can be installed via Bioconductor
# if (!requireNamespace("BiocManager", quietly = TRUE))
#     install.packages("BiocManager")
# BiocManager::install(version = "3.18")
# 
# # This should install goseq 1.54.0. Check this under the packages tab!
# BiocManager::install("goseq")
# BiocManager::install("org.Hs.eg.db")
# 
# # This last part will install some older version packages so that ComplexUpset will run properly
# devtools::install_version("dplyr", version = "1.1.3", repos = "http://cran.us.r-project.org")
# devtools::install_version("ggplot2", version = "3.4.4", repos = "http://cran.us.r-project.org")

We’re playing fast and loose with some installations here to get it to work on r.datatools.utoronto.ca so after the installation is complete, restart your session with Session > Restart R. Don’t refresh your browser or you’ll lose the installation.

# Packages to help tidy our data
library(tidyverse)
library(readxl)

# Packages for the graphical analysis section
library(viridis)

# Lecture 04 visualization packages
library(networkD3)
Error in library(networkD3): there is no package called 'networkD3'
library(GGally)
Error in library(GGally): there is no package called 'GGally'
library(gghighlight)
library(ggrepel)
library(ComplexUpset)
Error in library(ComplexUpset): there is no package called 'ComplexUpset'

Load these libraries separately to save on a little bit of memory usage. We’ll need every last drop we can get.

# GO term analysis packages if you managed to install goseq
library(goseq)
Error in library(goseq): there is no package called 'goseq'
library(org.Hs.eg.db)
Error in library(org.Hs.eg.db): there is no package called 'org.Hs.eg.db'

1.0.0 Working with large amounts of data

Until this point, the data sets we have used in lecture have been relatively simple epidemiological information. All of our observations are values like new cases or vaccinated individuals tied to dates or time periods. Whereas most of our observations were connected in some linear fashion, the data we examine today will have tens of thousands of observations, each representing a different gene!

Given the complexity of our data, we will discuss ways to sort, partition, visualize and interpret it.


1.1.0 Where does RNAseq data come from?

Many of you may be familiar with the idea of RNAseq data. It begins in the real world (or at the bench) with individuals/groups/conditions compared against a control state. Biological samples can be collected in time series or at a single endpoint. Each experimental condition should have it’s matching control or baseline profile for comparison, not to mention biological replicates!

After collecting your samples you still need to prepare and sequence your libraries, check your data quality, trim reads, map them to a transcriptome, estimate/normalize the expression of genes within each library and then compare the expression between libraries/samples to determine if there are transcriptional differences! All of these steps are beyond this class BUT if you plan on working with transcriptomes, you’ll eventually encounter this process. Today we’ll jump the line and work directly with differential expression (DE) data. That means we’ll be at the bottom right corner of the following diagram AKA, the end of the pipeline.

Image from Bioinformatics Workbook: RNA Sequencing Analysis


1.2.0 Use a Sankey diagram to illustrate your workflow

Sankey diagrams are named after Irish Captain Matthew Henry Phineas Riall Sankey, who first presented this type of visualization in 1898 to convey the energy efficiency of a steam engine!

With larger sets of data across multiple experiments, it can sometimes be helpful to use a Sankey diagram to explain the connection between different aspects of your samples. The key attribute of the Sankey diagram is that the width of a connection (flow) is proportional to the quantity represented. So the Sankey diagram is a flow diagram that also visually quantifies the dynamics of the relationships in your diagram. Think of it much like a stacked bar chart that can split or join with other bars at each connection.

A useful package to generate Sankey diagrams is networkD3 using the sankeyNetwork() function. To generate a Sankey diagram, you need information on two variables with a third optional one:

  • source: a starting point or for your flow
  • target: the end point of your flow
  • value: the number that will represent the width of your flow. Usually some quantity of contribution but this can also be an optional variable.

We’ll take the time to reconstruct this sankey plot in the following sections.

We’ll find a pre-made table that describes a theoretical RNAseq workflow based in the datasets we’ll be working with today. Some sections have been compressed for brevity but we’ll find all of that information in ./data/Lecture04_sankey_data.csv. Let’s begin by reading it in.

# Read in our Sankey diagram in ./data/Lecture04_sankey_data.csv 

sankey_info.df <- read_csv("data/Lecture04_sankey_data.csv")
Rows: 24 Columns: 3
-- Column specification ------------------------------------------------------------------------------------------------
Delimiter: ","
chr (2): source, target
dbl (1): value

i Use `spec()` to retrieve the full column specification for this data.
i Specify the column types or set `show_col_types = FALSE` to quiet this message.
# Check the structure
str(sankey_info.df)
spc_tbl_ [24 x 3] (S3: spec_tbl_df/tbl_df/tbl/data.frame)
 $ source: chr [1:24] "CoV" "CoV" "IVS" "RSV" ...
 $ target: chr [1:24] "A549" "NHBE" "A549" "A549" ...
 $ value : num [1:24] 0.25 0.25 0.25 0.25 0.7 0.2 0.4 0.4 0.15 0.15 ...
 - attr(*, "spec")=
  .. cols(
  ..   source = col_character(),
  ..   target = col_character(),
  ..   value = col_double()
  .. )
 - attr(*, "problems")=<externalptr> 
# Look a few rows
head(sankey_info.df)

From the above output we can see that one source may have multiple target values and vice versa.

1.2.1 Convert your flow data frame into a node data frame

Although our data frame sankey_info.df contains the information about our connections, the networkD3 package requires some mapping of the nodes within our data. Each unique source and target will be counted as a node and we’ll save that information into sankey_nodes.df.

# Combine our source and target lists, then take the unique set and put that into the "name" vector.

# sankey_nodes.df <- data.frame(name=c(as.character(sankey_info.df$source),      # Location of our source nodes
#                                      as.character(sankey_info.df$target)) %>%  # Location of our target nodes
#                               unique() # Generate the unique combination of the two sets
#                              )

# Combine our target and source nodes into a single vector
sankey_nodes.df <- c(sankey_info.df$source,
                     as.character(sankey_info.df$target)) %>%
  
  # Only keep the unique values
  unique() %>%
  
  # Put this into a data frame under a column called "name"
  data.frame(name = .)


# Look at the structure, it's just a list of the nodes
str(sankey_nodes.df)
'data.frame':   18 obs. of  1 variable:
 $ name: chr  "CoV" "IVS" "RSV" "A549" ...

1.2.2 Map your node names back to your source and target data with match()

Now that we have our node information (18 total unique nodes) stored basically as an identification number, we have to map it back to sankey_info.df. This is essentially like working with a factor that spans two columns instead of just one. To accomplish the mapping, we’ll use the match() method. The match() function takes two values:

  • x: the values to be matched

  • table: the values to be matched against

and returns a vector of length x where each integer value represents the position of its matched value from table. Unmatched values from x (ie not found in table) are set to nomatch.

match() vs “%in%”: The match() function and the matching operator %in% are very similar in idea. Whereas the former produced a set of matched positions between sets, the latter produces a logical vector indicating inclusion (TRUE) or exclusion (FALSE) from the intersect of the sets.

Let’s try with a quick example with our data

# Which nodes match from our source nodes to our collected nodes (18 total nodes)?
match(sankey_info.df$source, sankey_nodes.df$name)
 [1]  1  1  2  3  4  5  6  6  7  7  7  7  7  8  9 10 11 11  9 12 12 13 13 14

Note that our node information must be 0-indexed! R uses an indexing scheme that begins at 1 so we’ll need to offset that information that we generate as well.

# Add our sankey_nodes.df as index information that maps between it and `sankey_info.df`
sankey_info.df <-
sankey_info.df  %>% mutate(IDsource = match(.$source, sankey_nodes.df$name) -1,
                           IDtarget = match(.$target, sankey_nodes.df$name) -1) # nodes must be 0-indexed

# take a peek at what we've wrought
head(sankey_info.df)

1.2.3 Build your Sankey diagram with sankeyNetwork()

Now that we have our two necessary data frames, we can build our Sankey diagram with the sankeyNetwork() function. This function has a few parameters that we’ll want to set:

  • Links: the location of our source/target relationship information (sankey_info.df).

  • Nodes: the listing of nodes in our diagram (sankey_nodes.df).

  • Source: the name of the source column (IDsource).

  • Target: the name of our target column (IDtarget).

  • Value: the name of our “optional” value to build the width of flows (value).

  • NodeID: the names of our nodes (sankey_nodes.df$name).

There are an additional number of parameters that allow you to play with the visualization of this diagram including:

  • colourScale: categorical colour of your node. Works together with…

  • NodeGroup: for colouring your nodes based on an extra column in Nodes.

  • LinkGroup: for colouring your links based on an extra column in Links.

  • nodeWidth: the numeric width of each node.

For more information on these parameters and others, you can go here

# Build the Sankey diagram with all of our information

sankeyNetwork(Links = sankey_info.df, # Where is the "edge" information
              Nodes = sankey_nodes.df, # Where is the "node" information
              Source = "IDsource", # Where are the source node labels
              Target = "IDtarget", # Where are the target node labels
              Value = "value", # What value do we assign to the relationships/edges
              NodeID = "name", # Where are the labels for the nodes stored?
              sinksRight = TRUE, # Push the nodes to the right
              fontSize = 10)
Error in sankeyNetwork(Links = sankey_info.df, Nodes = sankey_nodes.df, : could not find function "sankeyNetwork"

1.3.0 Scatterplot matrices for some quick QA of your read counts

Stepping back one step in our flowchart, just prior to your DE analysis, you’ll have a set of read counts generated from RNA-Seq data. The counts are an estimation of each transcript within your data. Some programs may include the analysis of spliced isoforms and others may not.

Often in your RNA-Seq datasets, you should have multiple replicates of your data. A quick way to assess the quality of your datasets is to generate a scatterplot matrix. The scatterplot matrix is an all-by-all assessment of your experiments that generates a scatterplot of read counts for each pair-wise dataset.

A scatterplot matrix can help to quickly assess the quality and consistency of your data.

Figure taken from: Visualization methods for differential expression analysis. Rutter et al., 2019. BMC Bioinformatics 20: 458


1.3.1 Import your wide-format read count data.

We’ll begin by importing our data from Wyler2020_AEC_SARSCoV2_17AAG_readcounts.tsv. This tab-separated data file contains total RNA experiments from the infection of airway epithelial cells (AECs) under different conditions, treatments with inhibitors, and timepoints.

# Read in your read_count data
wyler_readcounts.df <- read_tsv("./data/Wyler2020_AEC_SARSCoV2_17AAG_readcounts.tsv")
Rows: 27011 Columns: 38
-- Column specification ------------------------------------------------------------------------------------------------
Delimiter: "\t"
chr  (1): gene
dbl (37): width, AECII_01_24h_DMSO-1, AECII_02_24h_DMSO-2, AECII_03_24h_DMSO...

i Use `spec()` to retrieve the full column specification for this data.
i Specify the column types or set `show_col_types = FALSE` to quiet this message.
str(wyler_readcounts.df, give.attr = FALSE)
spc_tbl_ [27,011 x 38] (S3: spec_tbl_df/tbl_df/tbl/data.frame)
 $ gene                        : chr [1:27011] "A1BG" "A1BG-AS1" "A1CF" "A2M" ...
 $ width                       : num [1:27011] 1766 2130 9529 4653 2187 ...
 $ AECII_01_24h_DMSO-1         : num [1:27011] 22 55 0 0 0 ...
 $ AECII_02_24h_DMSO-2         : num [1:27011] 19 20 0 1 0 ...
 $ AECII_03_24h_DMSO-3         : num [1:27011] 15 38 0 0 2 ...
 $ AECII_04_24h_200nM17AAG-1   : num [1:27011] 29 11 0 0 2 4740 0 0 882 0 ...
 $ AECII_05_24h_200nM17AAG-2   : num [1:27011] 27 15 0 0 0 ...
 $ AECII_06_24h_200nM17AAG-3   : num [1:27011] 30 17 0 0 2 4220 0 0 820 0 ...
 $ AECII_07_24h_S2_DMSO-1      : num [1:27011] 24 41 0 0 0 ...
 $ AECII_08_24h_S2_DMSO-2      : num [1:27011] 18 46 0 0 2 ...
 $ AECII_09_24h_S2_DMSO-3      : num [1:27011] 22 42 0 0 4 2750 0 0 739 0 ...
 $ AECII_10_24h_S2_200nM17AAG-1: num [1:27011] 23 13 0 1 3 ...
 $ AECII_11_24h_S2_200nM17AAG-2: num [1:27011] 25 24 0 0 1 ...
 $ AECII_12_24h_S2_200nM17AAG-3: num [1:27011] 18 28 1 0 1 ...
 $ AECII_13_48h_DMSO-1         : num [1:27011] 24 57 0 0 0 ...
 $ AECII_14_48h_DMSO-2         : num [1:27011] 38 56 0 0 0 1930 0 0 892 0 ...
 $ AECII_15_48h_DMSO-3         : num [1:27011] 33 64 0 0 0 ...
 $ AECII_16_48h_200nM17AAG-1   : num [1:27011] 29 35 0 0 1 ...
 $ AECII_17_48h_200nM17AAG-2   : num [1:27011] 20 24 0 0 0 ...
 $ AECII_18_48h_200nM17AAG-3   : num [1:27011] 23 28 0 0 0 ...
 $ AECII_19_48h_S2_DMSO-1      : num [1:27011] 13 45 0 0 0 ...
 $ AECII_20_48h_S2_DMSO-2      : num [1:27011] 22 36 0 0 0 ...
 $ AECII_21_48h_S2_DMSO-3      : num [1:27011] 21 30 0 0 1 ...
 $ AECII_22_48h_S2_200nM17AAG-1: num [1:27011] 31 29 0 0 1 ...
 $ AECII_23_48h_S2_200nM17AAG-2: num [1:27011] 36 23 0 0 1 ...
 $ AECII_24_48h_S2_200nM17AAG-3: num [1:27011] 20 19 1 0 0 ...
 $ AECII_25_72h_DMSO-1         : num [1:27011] 35 43 0 1 2 ...
 $ AECII_26_72h_DMSO-2         : num [1:27011] 26 38 0 0 1 997 0 0 753 0 ...
 $ AECII_27_72h_DMSO-3         : num [1:27011] 29 53 0 0 4 1230 0 0 953 0 ...
 $ AECII_28_72h_200nM17AAG-1   : num [1:27011] 32 57 0 0 2 ...
 $ AECII_29_72h_200nM17AAG-2   : num [1:27011] 41 49 0 0 2 1050 0 0 553 0 ...
 $ AECII_30_72h_200nM17AAG-3   : num [1:27011] 33 37 0 0 3 857 0 0 458 0 ...
 $ AECII_31_72h_S2_DMSO-1      : num [1:27011] 24 58 0 0 1 ...
 $ AECII_32_72h_S2_DMSO-2      : num [1:27011] 33 63 0 0 3 ...
 $ AECII_33_72h_S2_DMSO-3      : num [1:27011] 23 47 1 1 2 ...
 $ AECII_34_72h_S2_200nM17AAG-1: num [1:27011] 22 39 0 1 1 ...
 $ AECII_35_72h_S2_200nM17AAG-2: num [1:27011] 28 26 0 0 2 866 0 0 481 0 ...
 $ AECII_36_72h_S2_200nM17AAG-3: num [1:27011] 43 34 0 0 3 ...

1.3.2 Prepare your data as a clean read count matrix

Before we proceed to generating our scatterplot matrix, we need to select our data from our main dataset. In particular, the data from Wyler et al., that we imported as wyler_readcounts.df contains data from infecting human airway epithelial cells (AECs) and treating with either DMSO or 200nM of the HSP90 inhibitor, 17-AAG. With each condition there is a set of infected (S2) or uninfected AECs. For each of these 4 conditions, there are 3 replicates and 3 timepoints (24-, 48-, and 72-hours). In total that amounts to 36 sets of data.

For our purposes, we’ll:

  1. Compare 24h vs 72h infected (ie S2 = SARS-CoV2) DMSO-treated found in columns 9-11 and 33-35. We can either select by index or use a selection helper like matches() to incorporate a regular expression.

  2. Filter our reads to the range of 10-5000 using the if_all() function.

  3. Rename our columns to remove some redundant data (AECII_xx_)

Note that the if_all() function is a rather new addition to the tidyverse and is used to help filter on a predicate across multiple columns. This is very much like the across() helper function you may have used previously for summarizing data. Like the across() function, the if_all() helper function uses the following parameters:

  • .cols: the columns you wish to apply your filtering predicate upon.

  • .fns: the function or predicate you are filtering with.

You’ll notice our use of ~ again to anonymize the function.

readcounts_24hv72h <-
    wyler_readcounts.df %>% 

    # Select for just the columns withs SARS-CoV-2 infection and DMSO at 24 and 72h
    dplyr::select(matches(r"((24h|72h)_S2_DMSO)")) %>% 

    # Restrict our data analysis to just readcounts above 10 and below 5000
    filter(if_all(.cols = everything(), .fns = ~ .x >10 & .x < 5000)) %>% 

    # Rename the columns by removing the first portion: AECII_xx
    rename_with(., ~ str_replace(string = .x, 
                                 pattern = r"(\w*_\d*_)", 
                                 replace = ""))

# Take a peek at our resulting tibble
str(readcounts_24hv72h)
tibble [12,430 x 6] (S3: tbl_df/tbl/data.frame)
 $ 24h_S2_DMSO-1: num [1:12430] 24 41 2534 712 204 ...
 $ 24h_S2_DMSO-2: num [1:12430] 18 46 2484 654 205 ...
 $ 24h_S2_DMSO-3: num [1:12430] 22 42 2750 739 225 ...
 $ 72h_S2_DMSO-1: num [1:12430] 24 58 958 1065 147 ...
 $ 72h_S2_DMSO-2: num [1:12430] 33 63 1163 1081 156 ...
 $ 72h_S2_DMSO-3: num [1:12430] 23 47 835 1022 124 ...

1.3.3 Use ggpairs() to generate a scatterplot matrix

You may recall our working with the parallel coordinate plot from lecture 2. Rather than transform the data ourselves, we passed our data along to the ggparcoord() function from the GGally package. Well that package is here to simplify our lives again! Rather than generating the paired data between each experiment ourselves, we can pass along the read count matrix we just generated to the ggpairs() function. Parameters of the ggpairs() function that we’ll use include:

  • data: the dataset containing our data. It can include both numerical and categorical data.
  • mapping: the aesthetic mapping (aside from x and y) used for altering your format.
  • columns: which columns from data are used to generate the plot. Defaults to all columns.
  • title, xlab, ylab: the titles of your various graph components.
  • upper and lower: named lists that may contain the variables “continuous”, “combo”, “discrete”, and “na” used to determine how pairwise combinations of continuous and/or categorical data are plotted across the grid. You basically need to choose how these data combinations will be plotted.
  • diag: a named list that may only contain the variables “continuous”, “discrete”, and “na”. Each of which is set to a specific kind of plot type (ie “densityDiag”, “barDiag”, or “blankDiag”).

Moreover, you may recall that GGally works with the ggplot or is ggplot-extensible so we can use many of the same features from ggplot to fix some of the aesthetics of the result object. In the end, you’ll notice that this is basically a faceted scatterplot but the dataframe required to generate it is more complex than what we currently have. For more information on this function, you can check out the GGally manual or check out the list of great examples provided on the authors’ github.

# Create your matrix scatterplot with ggpairs
ggpairs(readcounts_24hv72h, 
        
        # Set the alpha of our points so we can see them better
        mapping = aes(alpha = 0.1), 
        
        # We'll play with the correlation text so the values are little larger than default for us
        upper = list(continuous = wrap("cor", size = 9))
       ) + # Set the alpha of our points so we can see them better
    # Change some of the theme aspects like text size
    theme(text = element_text(size = 20),
          # Rotate the x-axis tick text to be 
          axis.text.x = element_text(angle = 90, vjust = 0.5, hjust = 1))
Error in ggpairs(readcounts_24hv72h, mapping = aes(alpha = 0.1), upper = list(continuous = wrap("cor", : could not find function "ggpairs"

Work smarter, not harder: When it comes to certain complex visualizations, it is best to avoid re-inventing the wheel. There are so many packages available that accomplish a great number of different visualizations. The key is to knowing what kind of visualization you want to produce, and then checking if a package exists. While the ggplot package can accomplish quite a few basic and complexly layered visualizations, ones like the above scatterplot matrix require the generation and replacement of multiple plot types. This requires going to a deeper level of knowledge beyond this course. A nice package like GGally takes away those issues while offering the ability to still customize and alter your plot format to a certain extent.

1.3.4 Interpreting a scatterplot matrix

As you can see, the scatterplot matrix can provide a quick way to check that your replicates are similar, while your comparison conditions should show more variation between them. Looking at our above plot, the triangular sections of scatterplots in the top-left and lower-right represent the comparison of replicates in our experiment. These should produce scatter points that are close to the diagonal - meaning the replicates are quite similar in the data produced.

In the lower-left corner of the plot we find the comparison between our two conditions (24- vs 72-hour data) and you can see much more scatter away from the diagonal. If our conditions really do induce different transcriptional profiles, this is what we should expected to see.

Across the diagonal we see the density plot of our read counts. These aren’t particularly helpful with this dataset as the data predominantly appears to have low read counts even after we filter for a minimum of 10 reads per gene.

This technique is also a useful way to identify potentially mislabeled samples before processing your data for analysis. Changes from the above pattern can signal the mislabeling of samples or changes to the process of sample preparation.

Not just for expression analysis! While we have use the scatterplot matrices in this section to compare our RNAseq datasets, these plot formats have a much broader use in determining if any possible linear correlation may exist between continuous variables. It’s a quick way to look at a wide scope of variables for possible relationships without doing all the in-depth analysis first. Think of it as yet another helpful tool for exploratory data analysis!


1.4.0 What does differential expression data look like?

Digging down into your DE results, with the human genome, you are looking to identify trends in gene expression differences across 43,000 potential transcripts sequences - only half of which produce proteins. You can imagine that opening up a tabular file of that size could take a bit of time.

For each DE experiment there are a number of values generated. In a popular package like DESeq2 you will typically find:

Variable Description
Gene name The names of your genes which may be in different formats like Entrez ID, Ensembl, or gene symbol
Base mean Average of the normalized counts values, accounting for size factors, taken over all samples (and or replicates)
Log\(_{2}\) fold-change The effect size estimate - how much your gene’s expression has changed from control/base conditions
Log fold-change SE An estimate of effect size uncertainty
p-value Hypothesis test against H\(_{0}\) that no difference exists between sample groups
adjusted p-value An adjusted p-value after taking into account multiple testing between groups/samples

You can find more information on working with the DESeq2 package here


1.5.0 You can examine data on many scales

Once we have a set of data we can examine it from many aspects:

  1. Compare all genes within a sample and across samples
  2. Compare DE in a subset of genes within a sample and across samples
  3. Compare DE results based on gene function

Let’s explore these different aspects and the kind of plots we can generate to accomodate the size of our data set. First, however, we need to find a useful data set to work with.


1.5.1 Use the readxl library to open your .xlsx files

We’ve briefly touched on using this package in Lecture 01 to help read our Microsoft Excel-based data. Today’s data set from Blanc-Melo et al. (Cell 2020), comes to us in the form of an excel file. Let’s use some of the tools from this package to help open up our data file. We’ll start by peeking at how many sheets there are using excel_sheets().

# What are the sheets to open in our data set?
excel_sheets("data/Blanco-Melo2020Cell.Supp1.xlsx")
[1] "Legend"         "DESeq2_Results"
Legend

DESeq2_Results

1.5.2 Open each sheet separately with read_excel()

Now that we can see there are two sheets in our data, we can assign each one to a different data frame using the read_excel() command which contains the parameter sheet. We can use this to determine which sheet to load either based on it’s position (integer) or it’s name as specified by excel_sheets().

# Assign our legend 
blanco_legend.df <- read_excel("./data/Blanco-Melo2020Cell.Supp1.xlsx", sheet=1)

# Assign our data
blanco_data.df <- read_excel("./data/Blanco-Melo2020Cell.Supp1.xlsx", sheet=2)

str(blanco_legend.df)
tibble [20 x 3] (S3: tbl_df/tbl/data.frame)
 $ Field      : chr [1:20] "SARS-CoV-2(A549)_L2FC" "SARS-CoV-1_L2FC" "MERS-CoV_L2FC" "RSV_L2FC" ...
 $ Explanation: chr [1:20] "Fold expresion change Infected vs Control (logarithmic scale to base 2)" "Fold expresion change Infected vs Control (logarithmic scale to base 2)" "Fold expresion change Infected vs Control (logarithmic scale to base 2)" "Fold expresion change Infected vs Control (logarithmic scale to base 2)" ...
 $ Notes      : chr [1:20] "SARS-CoV-2 infection (A549 cells, MOI: 2, 24hpi)" "SARS-CoV-1 infection (MRC5 cells, MOI: 3, 24hpi Data from: GSE56192)" "MERS-CoV infection (MRC5 cells, MOI: 3, 24hpi Data from: GSE56192)" "RSV (A549 cells, MOI: 2, 24hpi)" ...
str(blanco_data.df)
tibble [23,710 x 21] (S3: tbl_df/tbl/data.frame)
 $ GeneName                              : chr [1:23710] "IFNL1" "IFNL2" "IFNL3" "IFNB1" ...
 $ SARS-CoV-1_L2FC                       : num [1:23710] 0 0 0 0 0 ...
 $ IAV_L2FC                              : num [1:23710] 1.37 1.379 1.815 0.468 0.24 ...
 $ MERS-CoV_L2FC                         : num [1:23710] 0 0.125 0 0 0.125 ...
 $ HPIV3_L2FC                            : num [1:23710] 7.84 6.84 6.23 6.44 2.62 ...
 $ RSV_L2FC                              : num [1:23710] 7.14 4.84 4.83 4.86 0 ...
 $ SARS-CoV-2(Calu-3)_L2FC               : num [1:23710] 7.24 7.88 7.47 8.7 3.33 ...
 $ SARS-CoV-2(A549)_L2FC                 : num [1:23710] 0 0 0 -0.127 0 ...
 $ SARS-CoV-2(A549-ACE2)LowMOI_L2FC      : num [1:23710] 0 0 0 0.388 0 ...
 $ SARS-CoV-2(A549-ACE2)HiMOI_L2FC       : num [1:23710] 5.08 5.25 5.05 5.76 1.54 ...
 $ SARS-CoV-2(A549-ACE2)-Ruxolitinib_L2FC: num [1:23710] 3.548 1.947 2.511 3.92 0.297 ...
 $ padj_SARS-CoV-1                       : num [1:23710] 1 1 1 1 1 ...
 $ padj_IAV                              : num [1:23710] 1.00 1.00 1.00 1.00 1.00 1.11e-06 1.00 1.00 1.00 1.00 ...
 $ padj_MERS-CoV                         : num [1:23710] 1 1 1 1 1 ...
 $ padj_HPIV3                            : num [1:23710] 2.62e-68 1.48e-48 3.66e-35 2.13e-42 1.00 ...
 $ padj_RSV                              : num [1:23710] 4.51e-44 9.74e-17 3.19e-16 4.92e-17 1.00 ...
 $ padj_SARS-CoV-2(Calu-3)               : num [1:23710] 2.53e-139 5.69e-107 5.51e-102 0.00 4.20e-11 ...
 $ padj_SARS-CoV-2(A549)                 : num [1:23710] 1 1 1 1 1 ...
 $ padj_SARS-CoV-2(A549-ACE2)LowMOI      : num [1:23710] 0 0 0 0 0 ...
 $ padj_SARS-CoV-2(A549-ACE2)HiMOI       : num [1:23710] 1.00 2.69e-20 2.37e-18 1.15e-28 1.00 ...
 $ padj_SARS-CoV-2(A549-ACE2)-Ruxolitinib: num [1:23710] 1.00 1.00 1.00 1.54e-12 1.00 ...

1.5.3 ABW: Always Be Wrangling (your data)

Yes, it looks like the legend has some helpful information about the dataset itself found in the second sheet. The DE data looks like it’s split across 20 columns encompassing 10 experimental data sets. For each data set, it looks like there is a Log\(_{2}\) fold-change value (L2FC) and an adjusted p-value (padj).

Let’s take a closer look at blanco_legend.df first. From it we can parse out some important information about the experiments themselves like the pathogen and host in each experimental set.

# Look at the entire legend
blanco_legend.df

1.5.4 Extract substring patterns and save them using str_match_all()

You may remember that we can easily match for patterns in our string using functions from the stringr package but with a function like str_match_all() you can also include matching groups to your pattern that can be captured. For each string match, this function will return a matrix that contains the complete matching pattern and any grouped patterns that are denoted by the () parentheses.

If a string contains multiple matches to your pattern, it will generate additional rows in the matrix for that string.

Looking at our Notes column of blanco_legend.df, it looks like it follows a regular pattern were we can extract the pathogen and host cell information

section Examples
Pathogen name SARS-CoV-2, RSV
Intermediate strings infection, infection with Ruxolitinib
Host cell type A549, A549-ACE2
# pattern match for pathogen and host information and save it into a datafram you can access later.

exp_info <-
  blanco_legend.df %>% 
  
  # Use dplyr::select because the select function has been overridden by another library
  dplyr::select(Notes) %>% 
  
  # Use str_match_all to grab the pattern we want, piece by piece
  # example string: SARS-CoV-2 infection (A549-ACE2 cells, MOI: 2, 24hpi)
  str_match_all(pattern = r"(([\w-]+)[\s\w]+\(([\w-]+)\s*cells)") %>% 
  
  # Set into a data frame
  as.data.frame() %>% 
  
  # Change the column names (would be X1, X2, X3 otherwise)
  `colnames<-`(c("notes", "pathogen", "host"))
Warning in stri_match_all_regex(string, pattern, omit_no_match = TRUE,
opts_regex = opts(pattern)): argument is not an atomic vector; coercing
  # Alternative code to set names: 
  # magrittr::set_colnames(c("notes", "pathogen", "host"))

# Take a look at the resulting data
exp_info
# Add this new information to our original legend information

blanco_exp_info.df <- 
  blanco_legend.df %>% 
  
  # Make some new columns with pathogen and host information
  mutate(pathogen = exp_info$pathogen, # Our pathogen information
         host = exp_info$host) %>% # Our host information 
  
  # Combine our pathogen and host information into a single column as well
  unite(col = ..., ..., sep="_", remove = FALSE) %>% 
  
  # Just keep the original Field column and the new ones
  dplyr::select(1, 4, 5, 6) 
Error in dplyr::select(., 1, 4, 5, 6): '...' used in an incorrect context
# Take a look at what we are working with
blanco_exp_info.df
Error in eval(expr, envir, enclos): object 'blanco_exp_info.df' not found

1.5.5 Convert our DE data table into long-format and join with our cell information

Now that we have catalogued our experimental information, let’s build that into a long-format version of our actual DE dataset found in blanco_data.df. The steps we’ll take to prepare our data are

  1. Convert from wide to long format with pivot_longer().

  2. Join with our experimental data information in blanco_exp_info.df

  3. Replace our experiment information (formerly column information) to a consistent format of experiment_value so we can split it out properly.

  4. Separate our experiment_value information. Currently we’ll have experiments mapping to both an L2FC and padj value

  5. Convert our data back out a little bit to a slightly wider format with pivot_wider()

blanco_data_long.df <-
  blanco_data.df %>% 
  
  # collapse all of the data columns
  pivot_longer(cols=(2:21), names_to = "experiment", values_to = "value") %>% 
  
  # add our experimental info to each observation. Note that each gene has multiple obs now.
  full_join(., blanco_exp_info.df, by=c("experiment" = ...)) %>% 
  
  # We need to fix all of the "padj_experiment" values to "experiment_padj" format
  # Note we could have renamed our columns BEFORE pivoting
  mutate(experiment = str_replace_all(.$experiment, 
                                      pattern=r"(...)", # Capture the groups
                                      # Put them back together in a switched order
                                      replacement = r"(...)")) %>%    
  
  # Now we want to split the experiment column into two: experiment and data_type (L2FC or padj)
  separate(experiment, c("experiment", "data_type"), sep="_", remove=TRUE) %>% 
  
  # Pivot out the data so that each observation for each experiment has an L2FC and padj value
  pivot_wider(names_from = data_type, values_from = value)
Error in is.data.frame(y): object 'blanco_exp_info.df' not found
blanco_data_long.df
Error in eval(expr, envir, enclos): object 'blanco_data_long.df' not found
# Don't forget to save your wrangled data!
save(blanco_data_long.df, file="./data/Lecture04_blanco.RData")
Error in save(blanco_data_long.df, file = "./data/Lecture04_blanco.RData"): object 'blanco_data_long.df' not found

2.0.0 Visual inspection of DE across a sample

After all of our data wrangling, you can see we have a data frame with nearly 1/4 million observations. This spans across 10 experimental conditions. From a broad level, we can look across entire data sets to identify genes of interest. There are a couple of ways to do this and we’ll begin with a standard set of plots that can convey the overall distribution of our data. While our view of the data is low-resolution we are still able to compare specific gene information centered around mean expression levels, magnitude of comparison, and statistical significance.


2.1.0 The MA plot describes the spread of our DE data

Used originally in the application of Microarray data analysis, these plots are also applied to visualizing high-throughput sequencing analysis.

This is a data transformation between two sets of data such that

  • M (minus in the log scale) represents the log\(_{2}\) ratio of fold differences between your data sets

  • A (average in the log scale) represents the average expression signal

We plot our M values along the y-axis against the corresponding A values on the x-axis. Do these characteristics sound familiar? They are standard output for DESeq2 data sets! Unfortunately our above data doesn’t have that information anymore, but we have a dataset that does! It’s another dataset from an earlier pre-publication copy of the Blanco-Melo et al., 2020 paper: Blanco-Melo2020_Supp_Data_4.xlsx.

Let’s open this up and take a look!

# We'll be looking at DE data for IAV infection of A549 cells
DE_A549_IAV.df <- read_excel(..., sheet=2)
Error in eval(expr, envir, enclos): '...' used in an incorrect context
# Look at the data frame we've made
head(DE_A549_IAV.df)
Error in head(DE_A549_IAV.df): object 'DE_A549_IAV.df' not found

2.1.1 ABmW: A Bit more Wrangling with across()

Okay, so we can’t use the data right away. The read_excel() command has decided to convert some of our columns to character format because it detected NaN values. So we’ll have to quickly coerce the data before moving on. We can quickly convert the data with a mutate() command using the helper verb across().

To successfully use this helper, we will want to list out the columns we want to use in the .cols parameter, and then apply a function like as.numeric. We don’t actually need all of these columns, but it’s good practice!

# Convert our numeric columns to the proper type.
DE_A549_IAV.df <- 
  DE_A549_IAV.df %>%
  
  # Use mutate() with across() to convert columns to numeric
  mutate(...)
Error in DE_A549_IAV.df %>% mutate(...): '...' used in an incorrect context
# Check the result
str(DE_A549_IAV.df)
Error in str(DE_A549_IAV.df): object 'DE_A549_IAV.df' not found

Skip the wrangling with a proper import! While we used the above import as an opportunity to practice our wrangling skills, we could have avoided it altogether if we imported properly. Within the read_excel() function there is a parameter called “na” which allows us to define the character(s) that represent null or NA values. The default is simply blank or empty cells but knowing what we do now about the dataframe, we could also have used set the parameter: na = “NaN” which would allow our dataframe to properly import the values and correctly assign variable types. So, it’s always helpful to know what your functions can do!


2.1.2 Build your MA plot using ggplot2

Given that we already have our data in a differential expression format, we can just use ggplot2 to plot the correct variables to the x and y axes. In this case, we are interested in using the log2FoldChange as our M and baseMean represents our A. There are a number of ways we can colour this plot but we’ll take into account our padj values to highlight those genes with DE values that are statistically significant.

Now, if we were using raw or normalized expression data, we would need to calculate the proper values for the transformation of M and A. There are a few packages that can take care of this for you such as ggmaplot which you can find here

For our plot, to differentiate between our points, let’s use the gghighlight package from last week! What will our predicates be based on? Log2 fold change? Adjusted p-value?

# MA plot! Need to select based on a fold change > x and padj <= fdr
# Usually FC = 1.5, fdr = 0.05 but we'll use the "status" variable here instead.

DE_A549_IAV.df %>% 
  filter(status == "OK") %>% 

  # 1. Data
  ggplot(.) +
    # 2. Aesthetics
    aes(x=baseMean, y = log2FoldChange) +
  
    # Theme
    theme_bw() +
    theme(text = element_text(size=20)) +
  
    # 3. Scaling
    # Use a log10 scale on the x axis
    scale_x_log10() + 
  
    # Set the breaks on our y-axis 
    scale_y_continuous(limits = c(-10, 10), breaks = seq(-10, 10, 2)) +
  
    # 4. Geoms
    # Colour our points all in red
    geom_point(colour = "red", alpha = 0.2, size=4, na.rm=TRUE) +
  
    ### 2.1.2
    # Change it so that only high L2FC with low p-values will be in red, the rest as black
    gghighlight(..., ..., unhighlighted_params = list(colour = "black"))
Error in filter(., status == "OK"): object 'DE_A549_IAV.df' not found

2.1.3 Interpreting our MA plot

So our plot shows us the general distribution of the DE data. Looking at it, we can see, notably, that it appears that we have many more genes upregulated than downregulated in comparison to our control. We also see, as expected, more low-probability variation occurring with lower normalized mean counts. Conversely, as our overall mean counts increase, we also see less variation in general but we do see a gene that does pass threshold DE with a low enough p-value! To investigate further you could filter the data or look again with a volcano plot!


2.2.0 Volcano plots examine fold-change vs p-value

While the MA plots explored data based on its fold-change values in relation to mean expression, the volcano plot looks purely at differential expression based on it’s p-value (ie the probability that the DE is real). To emphasize the importance of the p-value we will -log transform it so smaller p-values are higher up on the y-axis. This quickly partitions data points in the visualization to draw high-DE/low p-value combinations away from the bulk of the population.

To plot this data, we’ll return to our larger blanco_data_long.df which contains data from multiple experiments. For now, let’s focus on just the SARS-CoV-2 data that has been generated in the A549 host cells. Our -log transformation of the p-values will also be problematic when encountering 0, so be careful! Let’s compare plots with and without these problem values.

To label our points of interest, we’ll also use the geom_text_repel() from the ggrepel package. This will help arrange and plot text onto our scatterplot in a way that avoids collisions with the data and the other labels! You could also experiment with using the directlabels package that we played around with last week to accomplish a similar feat.

# Remind ourselves what our data table looks like
head(blanco_data_long.df)
Error in head(blanco_data_long.df): object 'blanco_data_long.df' not found
# Remove p-values = 0
volcano.plot1 <-

  # Volcano plot!
  blanco_data_long.df %>% 
  filter(pathogen == "SARS-CoV-2", host=="A549", padj !=0) %>% 
  
  # 1. Data
  ggplot(.) +
    # 2. Aesthetics
    aes(x=L2FC, y = -log10(padj)) +
  
    # Themes
    theme_bw()+
    theme(text = element_text(size=20)) +
    labs(title = "Volcano plot with p-values == 0 removed") +
  
    # 4. Geoms
    # points all coloured red
    geom_point(colour = "red", alpha = 0.7, size=4) +
  
    # highlight leaves only the ones meeting our criteria as red, colour rest as blue
    gghighlight(abs(L2FC) > 2, padj < 0.05, unhighlighted_params = list(colour = "lightblue")) +

    ### 2.2.0
    # Add labels but only for the top 10 genes
    geom_text_repel(data = blanco_data_long.df %>%
                        # Filter the data used to label
                        filter(... == "SARS-CoV-2", ...=="A549", abs(L2FC) > 2, padj !=0) %>%
                        # Arrange in ascending order
                        arrange(padj) %>%
                        # Take the first 10 results
                        dplyr::slice(1:10),
                    aes(label = ...), size=6)
Error in filter(., pathogen == "SARS-CoV-2", host == "A549", padj != 0): object 'blanco_data_long.df' not found
# leave in p-values = 0
volcano.plot2 <-

  # Volcano plot!
  blanco_data_long.df %>% 
  filter(pathogen == "SARS-CoV-2", host=="A549") %>% 
  
  # 1. Data
  ggplot(.) +
    # 2. Aesthetics
    aes(x=L2FC, y = -log10(padj)) +

    # Themes
    theme_bw()+
    theme(text = element_text(size=20)) +
    labs(title = "Volcano plot with all p-values") +

    # 4. Geoms
    # points all coloured red
    geom_point(colour = "red", alpha = 0.7, size=4) +

    # highlight leaves only the ones meeting our criteria as red, colour rest as blue
    gghighlight(abs(L2FC) > 2, padj < 0.05, unhighlighted_params = list(colour = "lightblue")) +

    # Add labels but only for the top 10 genes
    geom_text_repel(data = blanco_data_long.df %>% 
                        # Filter the data used to label
                        filter(pathogen == "SARS-CoV-2", host=="A549", abs(L2FC) > 2) %>% 
                        # Arrange in ascending order
                        arrange(padj) %>% 
                        # Take the first 10 results
                        dplyr::slice(1:10),
                    aes(label = GeneName), size=6)
Error in filter(., pathogen == "SARS-CoV-2", host == "A549"): object 'blanco_data_long.df' not found
# Plot both of our figures together
fig.show="hold"; out.width="50%"
volcano.plot1
Error in eval(expr, envir, enclos): object 'volcano.plot1' not found
volcano.plot2
Error in eval(expr, envir, enclos): object 'volcano.plot2' not found

2.2.1 Interpreting your volcano plots

Looking at our volcano plots, we can clearly see the volcano shape we are looking for. The data is split between our \(\pm\) log\(_{2}\) fold changes with points highlighted when we see DE beyond this. You can customize your parameters but you can also see the emphasis on lower p-values in our data set. The most “significant” data is found in the upper left and right quadrants of the graph, which wasn’t made clear in the MA plot. You could use the same tricks to label your MA plot too but the data could end up anywhere along the MA plot.

In the end, if you do have access to the original counts, it may be of use to consider this information when further investigating your candidate genes from DE.

Section 2.0.0 Comprehension Question: Comparing our two versions of the volcano plot, we see different sets of genes highlighted as the “top 10” based on adjusted p-values. What makes these two sets of data different? Why do we see datapoints at the top edge of our plots in the second version?


3.0.0 Medium resolution analysis focuses on groups of genes

Now that we’ve taken an overall look at our data, we can begin to assess our data based on some candidate genes or hypothesis testing. For instance, you may only be interested in looking at genes with an L2FC > 3. In other cases, you may be interested in a group of genes pertaining to a function like the chemokines which are part of the inflammation response.

After selecting a subset of genes we can begin to compare their DE data more meaningfully across different sample sets. Let’s continue working with our long-format dataset blanco_data_long.df which contains our multiple RNAseq experiments from various infection conditions. We will begin by generating a list of the highest DE genes with low p-values in the SARS-CoV-2 infection scenario.

Note here we’ll use the pull() function which is a nicer-looking way to retrieve a single column as a vector from our tibble object. This is equivalent to using a .$colName format.

# Select the genes we want to visualize
heatmap_gene.list <- 
  blanco_data_long.df %>% 
  
  # filter for data from a specific experiment, and then by high positive L2FC values with low p-values
  filter(pathogen == "SARS-CoV-2", host == "A549", L2FC > ..., padj < ...) %>% 
  ...
Error in eval(expr, envir, enclos): '...' used in an incorrect context
  # Take a look at the resulting list
  heatmap_gene.list
Error in eval(expr, envir, enclos): object 'heatmap_gene.list' not found

3.1.0 Use heatmaps to visualize data across a continuous range of values

Now that we have generated a select group of genes to examine, our DE data is well-suited to visualizing in heatmap or heat plot. In our heatplot we can generate values along two categorical axes. On the y-axis we can plot DE values from our individual genes, while on the x-axis we will show values across multiple experiments. To visualize the data itself, we will use the geom_tile() command.

# heatmap of our data based on a list of top DE genes from the SARS-CoV-2 infection of A549 cells

blanco_data_long.df %>% 
  filter(GeneName %in% heatmap_gene.list,
           host == "A549"
        ) %>% 
  # mutate(GeneName = factor(GeneName, levels = heatmap_gene.list)) %>% 
  
  # 1. Data
  ggplot(.) +
    #2. Aesthetics
    ### 3.1.0 set aesthetics
    aes(x=..., y = ..., fill=...) +

    # Theme
    theme_bw()+
    theme(text = element_text(size=18)
         )+
    labs(title = "Heatmap of L2FC values in A549 cells infected by different pathogens") +

    # 3. Scaling
    # Keep this to reverse the y axis or put it into the assignment?
    scale_y_discrete(limits = rev) + 
    scale_fill_viridis_c(option="plasma") +

    # Geoms
    ### 3.1.0 Use a tile and set the width a little smaller to give a gap between groups
    geom_tile(...)
Error in filter(., GeneName %in% heatmap_gene.list, host == "A549"): object 'blanco_data_long.df' not found
# heatmap of our data based on a list of top DE genes from the SARS-CoV-2 infection of A549 cells
# Split x-axis by experiment name

blanco_data_long.df %>% 
  filter(GeneName %in% heatmap_gene.list,
           ...
        ) %>% 
  
  # 1. Data
  ggplot(.) +
    #2. Aesthetics
    aes(x=experiment, y = GeneName, fill=L2FC) +

    # Theme
    theme_bw()+
    theme(text = element_text(size=18),
          axis.text.x = element_text(angle=45, hjust = 1)
         ) +
    labs(title = "Heatmap of L2FC values in SARS-CoV2 infections by experiment") +

    # 3. Scaling
    # Keep this to reverse the y axis or put it into the assignment?
    scale_y_discrete(limits = rev) + 
    scale_fill_viridis_c(option="plasma") +

    # Geoms
    # Use a tile and set the width a little smaller to give a gap between groups
    geom_tile(width = 0.95)
Error in ggplot(.): '...' used in an incorrect context

3.1.1 Interpreting a heatmap of upper values

From our list of the top 18 DE hits in our SARS-CoV-2 set, it looks like we see some similar hits across some of the genes when infecting A549 cells with other viruses like HPIV3 and RSV.

We see strong L2FC values for IL6, IL1A and LAMC2 for instance. IL6 (interleukin 6) has been reported to play a role in both mounting an effective immune response to certain viral infections but its interactions with other factors may also have a potential role in the exacerbation of viral phenotypes. In looking across different host cell infections by SARS-CoV-2 there is again consistent upregulation of IL1A and IL6.

Using a similar method, we can instead focus on genes from a specific family or pathway as long as we have a list. Let’s try the family of chemokines which play a role in inducing cell movement during the immune reponse.

# Build a list of the potential chemokines by looking for those matching the "CCL" pattern
heatmap_ccl.list <- 
  blanco_data_long.df %>% 
  
  # Filter specifically for any genes that have CCL in their name
  filter(...) %>% 
  pull(GeneName) %>% 
  unique()
Error in pull(., GeneName): '...' used in an incorrect context
str(heatmap_ccl.list)
Error in str(heatmap_ccl.list): object 'heatmap_ccl.list' not found

Now we can use this list in our ggplot visualization. Note that we could have generated a much longer pipe series without saving the object heatmap_ccl.list but then we wouldn’t have a chance to examine it before plotting that data to learn that it has 29 unique values.

# heatmap of our data based on a list of chemokine genes

blanco_data_long.df %>% 

  # Filter for genes in our list, and experiments where the host cell was "A549"
  filter(GeneName %in% ...,
           host == "A549"
        ) %>% 
  
  # 1. Data
  ggplot(.) +
    #2. Aesthetics
    aes(x=experiment, y = GeneName, fill=L2FC) +

    # Theme
    theme_bw()+
    theme(text = element_text(size=20),
          axis.text.x = element_text(angle=45, hjust = 1)
         )+

    # 3. Scaling
    scale_fill_viridis_c(option="plasma")+

    # Geoms
    # Use a tile and set the width a little smaller to give a gap between groups
    geom_tile(width = 0.95)
Error in filter(., GeneName %in% ..., host == "A549"): object 'blanco_data_long.df' not found

3.1.2 Heatmap interpretation

From our heatmap, we can observe that looking only at the chemokines, there is consistent upregulation of CCL5 and CCL2 when infecting our A459 cells with any of these viruses. In comparison, however, HPIV3 and RSV infection show a much higher DE across the CCL family in comparison to SARS-CoV-2 which appears to illicit less inflammatory response in the time-frame sampled.

But wait there’s more! While we are dealing with just a subset of our RNA-Seq data, much more is available to visualize on a greater scale. Next week we’ll dig deeper into high-dimensional data and how heatmaps can be used to visualize and organize this kind of information to help identify and convey patterns.


3.2.0 Looking at genes with dot plots

Note that in the above heatmap we no longer have any information on the adjusted p-values from these L2FC scores. Something to keep in mind when making these heatmaps. What would happen if we filtered on p-values as well? Is there a way we could track that information?

If we replace our geom_tile() with a geom_point() we are able to add an additional dimension to our data by utilizing size mapping.

We’ll repeat our visualization from above but use colour to represent the L2FC values and a size to represent the padj values in our dataset.

# heatmap of our data based on a list of chemokine genes

blanco_data_long.df %>% 

  # Filter for genes in our list, and experiments where the host cell was "A549"
  filter(GeneName %in% heatmap_ccl.list,
           host == "A549"
        ) %>% 
  
  # 1. Data
  ggplot(.) +
    #2. Aesthetics
    aes(x=experiment, y = GeneName) +

    # Theme
    theme_bw()+
    theme(text = element_text(size=20),
          axis.text.x = element_text(angle=45, hjust = 1)
         )+

    # 3. Scaling
    scale_colour_viridis_c(option="viridis", ) +
    scale_size_continuous(range = c(1, 10), breaks = c(0, 1.3, 10, 100)) +

    # Geoms
    ### 3.2.0 Use colour and size in our dotplot to represent L2FC and padj
    geom_point(aes(colour = ..., size = ...))
Error in filter(., GeneName %in% heatmap_ccl.list, host == "A549"): object 'blanco_data_long.df' not found

Now we can more clearly see which values are worth noting or investigating. We can see that CCL2 has a more consistent L2FC and padj combination although this would require further investigation since our size range is still quite limited. We can further zoom in on our data by specifically visualizing our genes of interest!

3.2.1 Looking at single genes with dot plots

We can further narrow our search instead of looking at whole gene groups, and pick a small set of specific genes. We’ll use a dotplot to visualize the data where the y-axis uses the L2FC value to place points and we’ll use a categorical x-axis of gene lists. We’ll filter our data to only include infections in the A549 host, and also group our data in a few ways:

  1. We’ll scale our point sizes based on the adjusted p-value to get a better sense of how probable these changes are.
  2. We’ll use colour to define the pathogen used in each experiment.
  3. We’ll also facet our data based on host.
single_genes = c("CCL5", "CCL2", "CCL20", "CCL17", "IL6", "IL1A")

blanco_data_long.df %>% 
  filter(GeneName %in% single_genes,
        ) %>% 
  
  # 1. Data
  ggplot(.) +
    # 2. Aesthetics
    ### 3.2.0 set the aesthetics for our dot plot colouring by pathogen, shaping by host
    aes(x=GeneName, y = L2FC, colour=..., shape=...) +

    # Theme
    theme_bw()+
    theme(text = element_text(size=20)) +

    # 3. Scaling
    scale_colour_viridis_d(direction = -1, 
                           guide = guide_legend(override.aes = list(size = 5))) +
    # Scale the size range
    scale_size(range = c(8, 4)) +
    scale_shape(guide = guide_legend(override.aes = list(size = 5))) +

    # 4. Geoms
    ### 3.2.0 adjust the dot size based on the adjusted p-value
    geom_point(aes(size = ...), alpha = 0.8, stroke = 2) +

    # 6. Facets
    facet_wrap(~ host, ncol = 2, scales = "free_y")
Error in filter(., GeneName %in% single_genes, ): object 'blanco_data_long.df' not found

3.2.1 Dot plot interpretation

Focusing on small sets of genes we can see some trends across our groups. For instance, 3 of our pathogens produce a strong IL6 response as we’ve seen in our heatmaps. We also see a >2 L2FC in CCL2 as a response to infection by SARS-CoV-2. Actually our weakest IL6 response to SARS-CoV-2 was seen in the ACE-2-expressing A459 hosts so our initial heatmap in section 3.1.0 didn’t give us a full picture of what might be happening.

It would be best to further investigate these on a more specific subset of hosts and/or pathogens.

Comprehension Question 3.0.0 While we see some inconsistency in our IL6/SARS-CoV-2 response in the above dotplot, are we possibly missing some context? If you were to return to the source of this data prior to our wrangling (see section 1.5.2), you might note that there are 3 kinds of A549-ACE2 experimental groups. What are some ways you could account for this information when wrangling?


4.0.0 RNAseq analysis for significant functional terms

Up until this point, we’ve been feeling our way through the data, rather… undirected. Often within manuscripts you will find deeper analyses of differential expression by examining the collection of functional terms to determine trends or pathways of interest. The Gene Ontology (GO) resource represents a codification of gene function through three domains: cellular component (CC), molecular function (MF), and biological process (BP).

Most, if not all, of the genes in our dataset can be described in some way by a GO term. Once we collect these terms, we can begin to look for meaningful groups that could be considered over-represented (or under-represented) in our dataset. Once we have collected this information we can visualize it similar to our individual gene dotplots.


4.1.0 Use the goseq package to pull down GO terms for your genes

Using the goseq package we can perform a full GO term analysis on our DE data with an analysis for enriched terms within our set of over- or under-expressed genes. However, in order to begin the process of pulling down GO terms, with the goseq package, we also need to generate a named integer vector to split our gene sets into the two relevant categories.

We’ll store our categorized set of genes in genes_SARS_CoV_2 where a 1 represents over/underexpressed genes in our set, and 0 represents all remaining genes.

# Set up the gene information we need for the GO-term analysis
# It needs to know which genes meet our FC criteria and which do not
#nrow(blanco_data.df)

# Casting our logical as an integer will create a 0/1 matrix
genes_SARS_CoV_2 <- as.integer(blanco_data.df$`padj_SARS-CoV-2(A549)` < 0.05 & # low p-value
                               abs(blanco_data.df$`SARS-CoV-2(A549)_L2FC`) > 1.5) # absolute L2FC > 1.5

# We'll name each element in our vector by the gene names
names(...) <- blanco_data.df$GeneName
Error in names(...) <- blanco_data.df$GeneName: '...' used in an incorrect context
# What is our object?
str(genes_SARS_CoV_2)
 int [1:23710] 0 0 0 0 0 1 0 0 1 0 ...
# Summarize our vector
table(genes_SARS_CoV_2)
genes_SARS_CoV_2
    0     1 
23232   478 

So a quick bit of filtering and we can see from our table summary, that we have close to 500 genes that are significantly differentially expressed above or below 1.5 L2FC. Now we have to figure out how to map these to GO terms!

4.1.1 Choose the correct database to map your gene symbols to GO terms

The goseq package is compatible with a number of organisms and gene names for mapping your data to the GO database. To view which organisms are supported, we can use supportedOrganisms() to view. Since the raw RNA-Seq reads were aligned to the human genome using the hg19 assembly (see Blanco-Melo et al., 2020), we’ll be working with hg19 as well. We’ll use the annotation information provided for the next part of our analysis.

First, however, let’s check that hg19 is indeed a supported genome annotation.

# Pull down the supported organisms and look for the human "hg19" genome
supportedOrganisms() %>% 
filter(...)
Error in supportedOrganisms() %>% filter(...): '...' used in an incorrect context

4.1.2 Fitting a probability weighting function (PWF) to our dataset

Looking at our table above we can immediately see that there are 3 potential annotations sets to draw from. Each uses a different kind of ID description: Entrez Gene ID, Ensemble gene ID, and Gene Symbol. Which one do we use? This depends on how our data is formatted but let’s take a quick look at this screenshot of CCL8 entry from NCBI:

It’s important to know the difference between your gene symbols and various identifiers!

Knowing what we’ve seen of the data already, the information we’re looking for is in the third row. Using the gene symbols in our data, we can

  1. Map our gene symbols to GO annotations, and
  2. Gather the gene lengths for our data.

The second portion of information is required in the generation of a probability weighting function (PWF). This PWF will determine a weighting for each gene which is essentially the probability of a gene being differentially expressed based on its length alone. It’s important to generate with information as it can influence our enrichment analysis. Ultimately we use the PWF to help inform the creation of a null distribution in our analysis.

We’ll use the nullp() function to generate the PWF for our dataset.

# Generate the PWF for our vector of genes
pwf_SARS_CoV_2 <- nullp(DEgenes = ...,      # Our list of DE genes
                        genome = ...,                 # The GO annotation we want to use
                        id = ...)               # The id we'll use to generate the data using gene symbols
Error in nullp(DEgenes = ..., genome = ..., id = ...): could not find function "nullp"
head(pwf_SARS_CoV_2)
Error in head(pwf_SARS_CoV_2): object 'pwf_SARS_CoV_2' not found

Looking at a plot of our PWF: here we see a subset of genes and their PWF values to see how they fit according to our line of best fit. The goal is for the points to be relatively close to our line of fit. If the values are far off, there may be an issue with the annotation data or how you calculated your gene lengths for the PWF.


4.1.3 Generate enriched GO terms from our DE data

We’re now ready to query the GO database for up- and down-regulated terms in our data. We’ll use the goseq() command to do this, providing our PWF object as well as the correct database information. The default method used in this process to generate our enrichment analysis is the Wallenius approximation. You can choose to use random sampling but this method can take much longer with little difference in results.

For more information on this process see the goseq package vignette here

# Generate our go enrichment analysis
GO.wall_SARS_CoV_2 <- goseq(pwf = ...,      # Provide our pwf object
                            genome = "hg19",           # Set the annotation set to use
                            id = "geneSymbol",         # Which variable will we use to compare with
                            method = ...       # How will you decide on the null distribution
                           )
Error in goseq(pwf = ..., genome = "hg19", id = "geneSymbol", method = ...): could not find function "goseq"
# What does the final result look like?
head(GO.wall_SARS_CoV_2)
Error in head(GO.wall_SARS_CoV_2): object 'GO.wall_SARS_CoV_2' not found
# What is the structure of our resulting data?
str(GO.wall_SARS_CoV_2)
Error in str(GO.wall_SARS_CoV_2): object 'GO.wall_SARS_CoV_2' not found

4.2.0 Interpreting our goseq results

Looking at the results of our analysis, we are returned a listing of 22,500 unique GO terms across the 3 domains (CC, BP, and MF). We are given two sets of p-values, one each for over-represented and under-represented terms. With each term we also see how many times that term is represented in our DE data vs the total number of times that term may appear in the GO database we queried. As you can see from the first few rows of data, some GO term categories can encompass a large number of genes.

Remember how we created this data! Let’s also remind ourselves that we set up our DE gene list using the data from A459 cells infected with SARS-CoV-2. Looking at the manuscript itself, while having a distinct immune response, the replication of the virus was very low in these cell types which lack the expression of the ACE2 receptor. We’re really just using this data to make some visualizations!

To begin our analysis we can filter the enriched GO terms sets by applying a multiple hypothesis test correction with p.adjust() using the Benjamini-Hochberg method to minimize the false discovery rate.

# Generate an enriched Go term dataset
enriched.GO_SARS_CoV_2 <- 
# Adjust the p-values of the over-represented column
# GO.wall_SARS_CoV_2[p.adjust(GO.wall_SARS_CoV_2$over_represented_pvalue, method="BH") < 0.05, ]

GO.wall_SARS_CoV_2 %>% 
filter(...)
Error in GO.wall_SARS_CoV_2 %>% filter(...): '...' used in an incorrect context
# How many terms come back from our filtering?
str(enriched.GO_SARS_CoV_2)
'data.frame':   84 obs. of  7 variables:
 $ category                : chr  "GO:0006261" "GO:0003688" "GO:0000727" "GO:0008283" ...
 $ over_represented_pvalue : num  2.86e-10 2.30e-09 3.83e-09 7.78e-09 2.07e-08 ...
 $ under_represented_pvalue: num  1 1 1 1 1 ...
 $ numDEInCat              : int  20 9 7 83 73 10 24 136 6 99 ...
 $ numInCat                : int  142 23 12 1967 1694 39 259 4174 12 2649 ...
 $ term                    : chr  "DNA-dependent DNA replication" "DNA replication origin binding" "double-strand break repair via break-induced replication" "cell population proliferation" ...
 $ ontology                : chr  "BP" "MF" "BP" "BP" ...
# Load our goseq data to look at and visualize
# This will also contain some additional datasets we'll want to look at.
load("./data/Lecture04.RData")
# What kind of terms do we see in our enrichment?
enriched.GO_SARS_CoV_2$...
NULL

4.2.1 Plot our enriched GO term categories as a dot plot

Looking at our enriched set of GO terms, we see terms like inflammatory response and response to stress. Now that we have a set of enriched GO terms, we can filter and plot this information as a dotplot with the following characteristics:

  1. GO terms listed along the y-axis
  2. size of dots representing category size
  3. use colour to represent the % of DE genes present from that that category

Let’s look at terms that include the word “response”.

enriched.GO_SARS_CoV_2 %>% 
  # Generate a percentage value 
  mutate(percentOfCat = numDEInCat/numInCat,
         # Create a dummy category
  #        pathogen = "SARS-CoV-2",
         # Make a set of adjusted p-values
         FDR = p.adjust(over_represented_pvalue, method = "BH")
      ) %>% 
  
  # Filter for only terms that include the word "response"
  filter(str_detect(term, "response")) %>% 
  
  # 1. Data
  ggplot(.) +
    # 2. Aesthetics
    ### 4.2.1 set aesthetics by numDEInCat and FDR
    aes(x="SARS-CoV-2", y=..., size=..., colour = ...) +
  
    # Theme
    theme_bw() +
    theme(text = element_text(size=20)) +
  
    # 3. Scaling
    scale_size(range=c(3,10)) +
    scale_colour_viridis_c(direction = -1) + 
  
    # 4. Geoms
    geom_point()
Error in eval(expr, envir, enclos): '...' used in an incorrect context

4.2.2 Plot multiple conditions to the same dot plot

With a little bit of elbow grease, we can generate a data frame with multiple GO enrichment analyses for multiple experiments and now we can compare enrichment across data sets! Our Lecture04.RData contained the combined experimental GO results in the object enriched.GO_combined.df.

# Take a look at our combined data frame
enriched.GO_combined.df
# Load up a combined enrichment analysis
# load("./data/Lecture04.RData")

enriched.GO_combined.df %>% 

  # 1. Data
  ggplot(.) +
    # 2. Aesthetics
    ### 4.2.4 Set the new x-axis value and colour aesthetic
    aes(x=..., y=term, size=numDEInCat, colour = ...) +
  
    # Theme
    theme_bw() +
    theme(text = element_text(size=20)) +
  
    # 3. Scaling
    scale_size(range=c(3,10)) +
    scale_colour_viridis_c(direction = -1) + 
  
    # 4. Geoms
    geom_point()
Error in eval(expr, envir, enclos): '...' used in an incorrect context

4.3.0 Upset plots

4.3.1 Can Upset plots help us to make sense of overlapping relationships?

Upset plots are an alternative to Venn diagrams that show the intersection of sets, as well as the size of the sets. Additionally, Venn diagrams can be difficult to interpret for greater than 2 or 3 sets. This is a real life figure from BMC Bioinformatics. Sure it looks pretty, but what does the number 24 represent in this picture in terms of A, B, C, D, and E?

What is the meaning of the value 24 in this diagram? Stare at it long enough and you’ll see which group it’s in but imagine this was 10 groups?

The UpSet plot was first published in 2014 and has become a helpful tool for visualizing the intersection between components with datasets. Along with the publication came a package for working with these plots. Known as the UpSetR package, there have since been more projects implementing this kind of visualization. While UpSetR has not been updated in nearly 3 years, a more active package known as ComplexUpset is available.

To some extent, the ComplexUpset has extensibility with ggplot, allowing you to use somewhat familiar syntax to modify these plots. Add to this the ability to stack or include additional visualizations of your datasets distributions or other characteristics, and this is a pretty attractive package to work with.


4.3.2 Working with the ComplexUpset package to visualize overlapping datasets

Let’s see how UpSet plots work practically. We can compare our data from blanco_data_long.df and compare the overlap of DE genes (after filtering or not). To do this in a practical sense, we again need to convert our values to a boolean representation after some more data wrangling. Our data wrangling steps will include:

  1. Generating a short-list of genes with a high log2 fold-change and low p-value.
  2. Filtering our data to include only those genes.
  3. Converting our data to a wide format where each column represents the inclusion status of a gene (observation) for a specific experiment.

Let’s look at our data structure again.

head(blanco_data_long.df)
Error in head(blanco_data_long.df): object 'blanco_data_long.df' not found

Begin by making our short-list of genes to filter by.

# filter for upregulated genes in our SARS-CoV-2/A549 data set and make a list of gene names
SARS_CoV_2.list <-
  blanco_data_long.df %>% 
  
  # Filter our data
  filter(pathogen == "SARS-CoV-2", 
         host == "A549", 
        ...) %>% 
  
  # Select just our gene names using a shorthand conversion
  pull(GeneName)
Error in pull(., GeneName): '...' used in an incorrect context
  # Less code than the following steps:
  # dplyr::select(GeneName) %>% unlist() %>% as.character()

SARS_CoV_2.list
Error in eval(expr, envir, enclos): object 'SARS_CoV_2.list' not found

Filter our blanco_data_long.df and pivot to a wide-format containing our logical values for inclusion.

# Generate our upset data
blanco_upset.df <-
  blanco_data_long.df %>% 
  
  # Filter our data list using our genes of interest
  filter(GeneName %in% ...) %>% 
  
  # convert our L2FC data to a logical based on a value of >2
  mutate(upregulated = ...) %>% 
  
  # Select just the gene names, experiments, and the logical variable
  dplyr::select(GeneName, experiment, ...) %>% 
  
  # Pivot our data wide so that each GeneName is now a row, each experiment is a column
  pivot_wider(names_from = ..., values_from = ...) %>% 
  
  # Convert to a dataframe for the ComplexUpset package (or it will throw an error)
  as.data.frame()
Error in as.data.frame(.): '...' used in an incorrect context
str(blanco_upset.df)
Error in str(blanco_upset.df): object 'blanco_upset.df' not found

4.3.3 Generating our upset data

As you can see from our data transformations, we now have all of our experiments listed in columns, with each gene represented in each row. As we look down each column we see a value of TRUE or FALSE used to differentiate if there was overexpression of that gene in that specific experimental context.

If we had not filtered based on some set of genes, we would have a data frame with more than 23K columns! Now that we have our genes presented in this way we can proceed to generate an upset plot.

Working with the upset() function to build our plot, we will concern ourselves with the following parameters:

  • data: a dataframe including binary columns that represent membership in each class.
  • intersect: a list of column names which will be used to generate the intersecting data.
  • name: the label shown below the intersection matrix.
  • height_ratio: ratio of the intersection matrix to the intersection height (default = 0.5).
  • width_ratio: ratio of the overall set size width to intersection matrix width (default = 0.3).
  • min_size and max_size: minimal and maximal number of observations for an intersection to be included.
  • min_degree and max_degree: minimal and maximal number of degrees for an intersection to be included.
  • n_intersections: the maximum number of intersections to display based on our min_* or max_* criteria.
  • themes: a parameter used to pass theme changes through the upset_default_themes() or upset_modify_themes() functions.
# Generate our upset plot

upset(data = ...,                       # Provide our dataset
      intersect = ...,  # Name the columns we want to analyse 
      name='Experimental condition',                # Set the label below the intersection matrix
      width_ratio=0.1,                              # Make the Set size width a little smaller
      min_size = ...,                                 # Require there to be a minimum of 2 members to show an intersection
      n_intersections = ...,                         # Set the max number of intersections we want to plot
      themes = upset_default_themes(text = element_text(size = 20)) # Set the plot text size to 20
     )
Error in upset(data = ..., intersect = ..., name = "Experimental condition", : could not find function "upset"

4.3.4 Interpreting our Upset plot

From our upset plot, we can see there are 3 regions.

  1. The left-hand barplot denotes the total number of observations in each set/category. In this case we’re talking about experimental conditions.

  2. The bottom plot graphically represents the different combinations of each category.

  3. The upper barplot displays the number of occurences for the combination displayed in the bottom plot.

From our set sizes, it looks like a L2FC value of > 2 was perhaps too stringent for the IAV and MERS-CoV DE data. Since we did filter our genes initially by the best hits in our SARS-COV-2(A549) dataset, we can see that it has the highest frequency group at 34 genes with over expression in just this set. The next largest overlapping set is of 14 genes between the SARS-CoV-2(A549) set and the RSV(A549) DE data.

You can also see from the plot that there are only 9 combinations of datasets with any real overlap. Had we set min_size = 1 instead, the remainder of the grouping combinations would be seen to contain just a single gene in each.

Overall this can make for a simplified interpretation of overlapping genes versus more complicated Venn diagrams!


5.0.0 Class summary

Today we took a long look at some popular visualizations for expression data sets generated by an experiment like RNAseq. Our analyses focused more on highlighting aspects of the differential expression information itself from a big-picture low-resolution view like volcano plots to zooming down to the individual gene level with dot plots.

Next week we’ll look at examples of “big” data from an even broader sense by using dimensional reduction techniques like classifying our datasets into groups with principle component analysis.


5.1.0 Weekly assignment

This week’s assignment will be found under the current lecture folder under the “assignment” subfolder. It will include an R markdown notebook that you will use to produce the code and answers for this week’s assignment. Please provide answers in markdown or code cells that immediately follow each question section.

Assignment breakdown
Code 50% - Does it follow best practices?
- Does it make good use of available packages?
- Was data prepared properly
Answers and Output 50% - Is output based on the correct dataset?
- Are groupings appropriate
- Are correct titles/axes/legends correct?
- Is interpretation of the graphs correct?

Since coding styles and solutions can differ, students are encouraged to use best practices. Assignments may be rewarded for well-coded or elegant solutions.

You can save and download the markdown notebook in its native format. Submit this file to the the appropriate assignment section by 12:59 pm on the date of our next class: April 11th, 2024.


5.2.0 Acknowledgements

Revision 1.0.0: created and prepared for CSB1021H S LEC0141, 03-2021 by Calvin Mok, Ph.D. Bioinformatician, Education and Outreach, CAGEF.

Revision 1.0.1: edited and prepared for CSB1020H S LEC0141, 03-2022 by Calvin Mok, Ph.D. Bioinformatician, Education and Outreach, CAGEF.

Revision 1.0.2: edited and prepared for CSB1020H S LEC0141, 03-2023 by Calvin Mok, Ph.D. Bioinformatician, Education and Outreach, CAGEF.

Revision 2.0.0: Revised and prepared for CSB1020H S LEC0141, 03-2024 by Calvin Mok, Ph.D. Bioinformatician, Education and Outreach, CAGEF.


5.3.0 References

Sankey diagram documentation: https://www.rdocumentation.org/packages/networkD3/versions/0.4/topics/sankeyNetwork

riverplot, another package for making your Sankey diagrams: https://cran.r-project.org/web/packages/riverplot/riverplot.pdf

MA plots directly from two sets of expression data: https://rpkgs.datanovia.com/ggpubr/reference/ggmaplot.html

Generating goseq data for RNA analysis: https://bioconductor.org/packages/devel/bioc/vignettes/goseq/inst/doc/goseq.pdf


The Center for the Analysis of Genome Evolution and Function (CAGEF)

The Centre for the Analysis of Genome Evolution and Function (CAGEF) at the University of Toronto offers comprehensive experimental design, research, and analysis services in microbiome and metagenomic studies, genomics, proteomics, and bioinformatics.

From targeted DNA amplicon sequencing to transcriptomes, whole genomes, and metagenomes, from protein identification to post-translational modification, CAGEF has the tools and knowledge to support your research. Our state-of-the-art facility and experienced research staff provide a broad range of services, including both standard analyses and techniques developed by our team. In particular, we have special expertise in microbial, plant, and environmental systems.

For more information about us and the services we offer, please visit https://www.cagef.utoronto.ca/.

LS0tDQp0aXRsZTogJycNCm91dHB1dDoNCiAgaHRtbF9ub3RlYm9vazogZGVmYXVsdA0KICBwZGZfZG9jdW1lbnQ6IGRlZmF1bHQNCi0tLQ0KDQpgYGB7ciwgZWNobz1GQUxTRX0NCiMgVGhpcyBhbGxvd3MgdGhlIGZpbGUgdG8gYmUgTElWRSBhbmQgcnVuIHdpdGhvdXQgZXJyb3JzIHN0b3BwaW5nIGl0Lg0Ka25pdHI6Om9wdHNfY2h1bmskc2V0KGVycm9yID0gVFJVRSkNCmBgYA0KDQo6Ojoge2FsaWduPSJjZW50ZXIifQ0KPGltZyBzcmM9Imh0dHBzOi8vZ2l0aHViLmNvbS9jYW1vay9DU0JfQ291cnNlX01hdGVyaWFscy9ibG9iL21haW4vQWR2Vml6L0NBR0VGX3NlcnZpY2VzX3NsaWRlLnBuZz9yYXc9dHJ1ZSIgd2lkdGg9IjcwMCIvPg0KOjo6DQoNCiMgQWR2YW5jZWQgR3JhcGhpY3MgYW5kIERhdGEgVmlzdWFsaXphdGlvbiBpbiBSDQoNCiMgTGVjdHVyZSAwNDogRXhwcmVzc2luZyBleHByZXNzaW9uIGRhdGENCg0KLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQoNCiMjIDAuMS4wIEFuIG92ZXJ2aWV3IG9mIEFkdmFuY2VkIEdyYXBoaWNzIGFuZCBEYXRhIFZpc3VhbGl6YXRpb24gaW4gUg0KDQoqKiJBZHZhbmNlZCBHcmFwaGljcyBhbmQgRGF0YSBWaXN1YWxpemF0aW9uIGluIFIiKiogaXMgYnJvdWdodCB0byB5b3UgYnkgdGhlIENlbnRyZSBmb3IgdGhlIEFuYWx5c2lzIG9mIEdlbm9tZSBFdm9sdXRpb24gJiBGdW5jdGlvbidzIChDQUdFRikgYmlvaW5mb3JtYXRpY3MgdHJhaW5pbmcgaW5pdGlhdGl2ZS4gVGhpcyBDU0IxMDIxIHdhcyBkZXZlbG9wZWQgdG8gZW5oYW5jZSB0aGUgc2tpbGxzIG9mIHN0dWRlbnRzIHdpdGggYmFzaWMgYmFja2dyb3VuZHMgaW4gUiBieSBmb2N1c2luZyBvbiBhdmFpbGFibGUgcGhpbG9zb3BoaWVzLCBtZXRob2RzLCBhbmQgcGFja2FnZXMgZm9yIHBsb3R0aW5nIHNjaWVudGlmaWMgZGF0YS4gV2hpbGUgdGhlIGRhdGFzZXRzIGFuZCBleGFtcGxlcyB1c2VkIGluIHRoaXMgY291cnNlIHdpbGwgYmUgY2VudHJlZCBvbiBTQVJTLUNvVi0yIGVwaWRlbWlvbG9naWNhbCBhbmQgZ2Vub21pYyBkYXRhLCB0aGUgbGVzc29ucyBsZWFybmVkIGhlcmVpbiB3aWxsIGJlIGJyb2FkbHkgYXBwbGljYWJsZS4NCg0KVGhpcyBsZXNzb24gaXMgdGhlIGZvdXJ0aCBpbiBhIDYtcGFydCBzZXJpZXMuIFRoZSBhaW0gZm9yIHRoZSBlbmQgb2YgdGhpcyBzZXJpZXMgaXMgZm9yIHN0dWRlbnRzIHRvIHJlY29nbml6ZSBob3cgdG8gaW1wb3J0LCBmb3JtYXQsIGFuZCBkaXNwbGF5IGRhdGEgYmFzZWQgb24gdGhlaXIgaW50ZW5kZWQgbWVzc2FnZSBhbmQgYXVkaWVuY2UuIFRoZSBmb3JtYXQgYW5kIHN0eWxlIG9mIHRoZXNlIHZpc3VhbGl6YXRpb25zIHdpbGwgaGVscCB0byBpZGVudGlmeSBhbmQgY29udmV5IHRoZSBrZXkgbWVzc2FnZShzKSBmcm9tIHRoZWlyIGV4cGVyaW1lbnRhbCBkYXRhLg0KDQpUaGUgc3RydWN0dXJlIG9mIHRoZSBjbGFzcyBpcyBhICoqY29kZS1hbG9uZyBzdHlsZSoqIGluIFIgbWFya2Rvd24gbm90ZWJvb2tzLiBBdCB0aGUgc3RhcnQgb2YgZWFjaCBsZWN0dXJlLCBza2VsZXRvbiB2ZXJzaW9ucyBvZiB0aGUgbGVjdHVyZSB3aWxsIGJlIHByb3ZpZGVkIGZvciB1c2Ugb24gdGhlIFtVbml2ZXJzaXR5IG9mIFRvcm9udG8gZGF0YXRvb2xzIEh1Yl0oaHR0cHM6Ly9kYXRhdG9vbHMudXRvcm9udG8uY2EpIHNvIHN0dWRlbnRzIGNhbiBwcm9ncmFtIGFsb25nIHdpdGggdGhlIGluc3RydWN0b3IuDQoNCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KDQojIyAwLjIuMCBMZWN0dXJlIG9iamVjdGl2ZXMNCg0KVGhpcyB3ZWVrIHdpbGwgZm9jdXMgb24gc3RhbmRhcmQgdmlzdWFsaXphdGlvbnMgb2YgZXhwcmVzc2lvbiBkYXRhLiBXaGlsZSBvdXIgcHJpb3IgdG9waWNzIGhhdmUgZm9jdXNlZCBvbiBtYWtpbmcgdmlzdWFsaXphdGlvbnMgdGhhdCBhcmUgYXBwbGljYWJsZSBicm9hZGx5IHRvIG1hbnkgdHlwZXMgb2YgZGF0YSwgYSBudW1iZXIgb2YgdGhpcyB3ZWVrJ3MgdmlzdWFsaXphdGlvbnMgYXJlIHVzZWQgbWFpbmx5IG9uIG11bHRpLWRpbWVuc2lvbmFsIGRhdGEgZnJvbSBleHBlcmltZW50cyBsaWtlIFJOQXNlcS4NCg0KQXQgdGhlIGVuZCBvZiB0aGlzIGxlY3R1cmUgeW91IHdpbGwgaGF2ZSBjb3ZlcmVkIHRoZSBmb2xsb3dpbmcgdG9waWNzDQoNCjEuICBWaXN1YWxpemluZyBmbG93Y2hhcnRzIGFuZCB3b3JrZmxvd3MNCjIuICBTY2F0dGVycGxvdCB2YXJpYW50cyBvZiBSTkFzZXEgZGF0YQ0KMy4gIENvbG91ciBncmFkaWVudCB2aXN1YWxpemF0aW9ucyBha2EgaGVhdG1hcHMNCjQuICBSTkFzZXEgZGF0YSBhbmFseXNpcyB3aXRoIGBnb3NlcWANCjUuICBWaXN1YWxpemluZyByZWxhdGlvbnNoaXBzIGJldHdlZW4gc2FtcGxlcw0KDQotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCg0KIyMgMC4zLjAgQSBsZWdlbmQgZm9yIHRleHQgZm9ybWF0IGluIFIgbWFya2Rvd24NCg0KYGdyZXkgYmFja2dyb3VuZGAgLSBhIHBhY2thZ2UsIGZ1bmN0aW9uLCBjb2RlLCBjb21tYW5kIG9yIGRpcmVjdG9yeS4gQmFja3RpY2tzIGFyZSBhbHNvIHVzZSBmb3IgaW4tbGluZSBjb2RlLlwNCippdGFsaWNzKiAtIGFuIGltcG9ydGFudCB0ZXJtIG9yIGNvbmNlcHQgb3IgYW4gaW5kaXZpZHVhbCBmaWxlIG9yIGZvbGRlclwNCioqYm9sZCoqIC0gaGVhZGluZyBvciBhIHRlcm0gdGhhdCBpcyBiZWluZyBkZWZpbmVkXA0KW2JsdWUgdGV4dF17c3R5bGU9ImNvbG9yOmJsdWUifSAtIG5hbWVkIG9yIHVubmFtZWQgaHlwZXJsaW5rDQoNCmAuLi5gIC0gV2l0aGluIGVhY2ggY29kaW5nIGNlbGwgdGhpcyB3aWxsIGluZGljYXRlIGFuIGFyZWEgb2YgY29kZSB0aGF0IHN0dWRlbnRzIHdpbGwgbmVlZCB0byBjb21wbGV0ZSBmb3IgdGhlIGNvZGUgY2VsbCB0byBydW4gY29ycmVjdGx5Lg0KDQo6Ojogey5hbGVydCAuYWxlcnQtYmxvY2sgLmFsZXJ0LWluZm99DQoqKkJsdWUgYm94OioqIEEga2V5IGNvbmNlcHQgdGhhdCBpcyBiZWluZyBpbnRyb2R1Y2VkDQo6OjoNCg0KOjo6IHsuYWxlcnQgLmFsZXJ0LWJsb2NrIC5hbGVydC13YXJuaW5nfQ0KKipZZWxsb3cgYm94OioqIFJpc2sgb3IgY2F1dGlvbg0KOjo6DQoNCjo6OiB7LmFsZXJ0IC5hbGVydC1ibG9jayAuYWxlcnQtc3VjY2Vzc30NCioqR3JlZW4gYm94ZXM6KiogUmVjb21tZW5kZWQgcmVhZHMgYW5kIHJlc291cmNlcyB0byBsZWFybiBQeXRob24NCjo6Og0KDQo6Ojogey5hbGVydCAuYWxlcnQtYmxvY2sgLmFsZXJ0LWRhbmdlcn0NCioqUmVkIGJveGVzOioqIEEgY29tcHJlaGVuc2lvbiBxdWVzdGlvbiB3aGljaCBtYXkgb3IgbWF5IG5vdCBpbnZvbHZlIGEgY29kaW5nIGNlbGwuIFlvdSB1c3VhbGx5IGZpbmQgdGhlc2UgYXQgdGhlIGVuZCBvZiBhIHNlY3Rpb24uDQo6OjoNCg0KLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQoNCiMjIDAuNC4wIExlY3R1cmUgYW5kIGRhdGEgZmlsZXMgdXNlZCBpbiB0aGlzIGNvdXJzZQ0KDQojIyMgMC40LjEgV2Vla2x5IExlY3R1cmUgYW5kIHNrZWxldG9uIGZpbGVzDQoNCkVhY2ggd2VlaywgbmV3IGxlc3NvbiBmaWxlcyB3aWxsIGFwcGVhciB3aXRoaW4geW91ciBSU3R1ZGlvIGZvbGRlcnMuIFdlIGFyZSBwdWxsaW5nIGZyb20gYSBHaXRIdWIgcmVwb3NpdG9yeSB1c2luZyB0aGlzIFtSZXBvc2l0b3J5IGdpdC1wdWxsIGxpbmtdKGh0dHBzOi8vci5kYXRhdG9vbHMudXRvcm9udG8uY2EvaHViL3VzZXItcmVkaXJlY3QvZ2l0LXB1bGw/cmVwbz1odHRwcyUzQSUyRiUyRmdpdGh1Yi5jb20lMkZjYW1vayUyRjIwMjQtMDMtQWR2X0dyYXBoaWNzX1ImdXJscGF0aD1yc3R1ZGlvJTJGJmJyYW5jaD1tYWluKS4gU2ltcGx5IGNsaWNrIG9uIHRoZSBsaW5rIGFuZCBpdCB3aWxsIHRha2UgeW91IHRvIHRoZSBbVW5pdmVyc2l0eSBvZiBUb3JvbnRvIGRhdGF0b29scyBIdWJdKGh0dHBzOi8vZGF0YXRvb2xzLnV0b3JvbnRvLmNhKS4gWW91IHdpbGwgbmVlZCB0byB1c2UgeW91ciBVVE9SaWQgY3JlZGVudGlhbHMgdG8gY29tcGxldGUgdGhlIGxvZ2luIHByb2Nlc3MuIEZyb20gdGhlcmUgeW91IHdpbGwgZmluZCBlYWNoIHdlZWsncyBsZWN0dXJlIGZpbGVzIGluIHRoZSBkaXJlY3RvcnkgYC8yMDI0LTAzLUFkdl9HcmFwaGljc19SL0xlY3R1cmVfWFhgLiBZb3Ugd2lsbCBmaW5kIGEgcGFydGlhbGx5IGNvZGVkIGBza2VsZXRvbi5SbWRgIGZpbGUgYXMgd2VsbCBhcyBhbGwgb2YgdGhlIGRhdGEgZmlsZXMgbmVjZXNzYXJ5IHRvIHJ1biB0aGUgd2VlaydzIGxlY3R1cmUuDQoNCkFsdGVybmF0aXZlbHksIHlvdSBjYW4gZG93bmxvYWQgdGhlIFItTWFya2Rvd24gTm90ZWJvb2sgKGAuUm1kYCkgYW5kIGRhdGEgZmlsZXMgZnJvbSB0aGUgUlN0dWRpbyBzZXJ2ZXIgdG8geW91ciBwZXJzb25hbCBjb21wdXRlciBpZiB5b3Ugd291bGQgbGlrZSB0byBydW4gaW5kZXBlbmRlbnRseSBvZiB0aGUgVG9yb250byB0b29scy4NCg0KIyMjIDAuNC4yIExpdmUtY29kaW5nIEhUTUwgcGFnZQ0KDQpBIGxpdmUgbGVjdHVyZSB2ZXJzaW9uIHdpbGwgYmUgYXZhaWxhYmxlIGF0IFtjYW1vay5naXRodWIuaW9dKGh0dHBzOi8vY2Ftb2suZ2l0aHViLmlvLzIwMjQtMDMuQWR2X0dyYXBoaWNzX1IvaW5kZXguaHRtbCkgdGhhdCB3aWxsIHVwZGF0ZSBhcyB0aGUgbGVjdHVyZSBwcm9ncmVzc2VzLiBCZSBzdXJlIHRvIHJlZnJlc2ggdG8gdGFrZSBhIGxvb2sgaWYgeW91IGdldCBsb3N0IQ0KDQojIyMgMC40LjMgUG9zdC1sZWN0dXJlIFBERnMNCg0KQXMgbWVudGlvbmVkIGFib3ZlLCBhdCB0aGUgZW5kIG9mIGVhY2ggbGVjdHVyZSB0aGVyZSB3aWxsIGJlIGEgY29tcGxldGVkIHZlcnNpb24gb2YgdGhlIGxlY3R1cmUgY29kZSByZWxlYXNlZCBhcyBhIFBERiBmaWxlIHVuZGVyIHRoZSBNb2R1bGVzIHNlY3Rpb24gb2YgUXVlcmN1cy4NCg0KIyMjIDAuNC40IERhdGEgdXNlZCBpbiB0aGlzIGxlc3Nvbg0KDQpUb2RheSdzIGRhdGFzZXRzIHdpbGwgZm9jdXMgb24gZGlmZmVyZW50aWFsIGV4cHJlc3Npb24gYW5hbHlzaXMuIFRoZSBiYXNpYyB2aXN1YWxpemF0aW9ucyBvZiB0aGlzIGRhdGEgYWZ0ZXIgaXQgaGFzIGFscmVhZHkgYmVlbiBwcm9jZXNzZWQgYnkgcGFja2FnZXMgbGlrZSBgREVTZXEyYC4NCg0KIyMjIDAuNC40LjEgRGF0YXNldCAxOiBMZWN0dXJlMDRfc2Fua2V5X2RhdGEuY3N2DQoNClRoaXMgaXMgYW4gZXhhbXBsZSBkYXRhc2V0IHVzZWQgZm9yIGJ1aWxkaW5nIGEgU2Fua2V5IGRpYWdyYW0uIEl0IGJ1aWxkcyBhIHRoZW9yZXRpY2FsIHdvcmtmbG93IGZvciBSTkFzZXEgYW5hbHlzaXMgYW5kIHZpc3VhbGl6YXRpb24gd2l0aGluIGEgbWFudXNjcmlwdC4gV2hhdCBpcyBhIFNhbmtleSBkaWFncmFtPyBXZSdsbCBmaW5kIG91dCENCg0KIyMjIDAuNC40LjIgRGF0YXNldCAyOiBXeWxlcjIwMjBfQUVDX1NBUlNDb1YyXzE3QUFHX3JlYWRjb3VudHMudHN2DQoNClJOQS1TZXEgcmVhZCBjb3VudCBkYXRhIGdlbmVyYXRlZCBmcm9tIFNBUlMtQ29WMiBpbmZlY3Rpb25zIG9mIEFFQyBjZWxscy4gVXNlZCB0byBjb21wYXJlIHRoZSB0aW1lY291cnNlIG9mIGV4cHJlc3Npb24gKHByby1pbmZsYW1tYXRvcnkpIGNoYW5nZXMgaW4gc2FtcGxlcyB0cmVhdGVkIHdpdGggYW5kIHdpdGhvdXQgSFNQOTAgaW5oaWJpdG9ycy4gUHVibGlzaGVkIGluICppU2NpZW5jZSogZG9pOiA8aHR0cHM6Ly9kb2kub3JnLzEwLjEwMTYvai5pc2NpLjIwMjEuMTAyMTUxPg0KDQojIyMgMC40LjQuMyBEYXRhc2V0IDM6IEJsYW5jby1NZWxvMjAyMENlbGwuU3VwcDEueGxzeA0KDQpUaGlzIGlzIGFuIFJOQXNlcSBkYXRhc2V0IGNvbXBhcmlzb24gZm9yIGluZmVjdGlvbiBvZiBtdWx0aXBsZSBjZWxsIHR5cGVzIHdpdGggcGF0aG9nZW5zIGxpa2UgU0FSUy1Db1YtMiwgU0FSUy1Db1YtMSwgTUVSUy1Db1YsIFJTViAocmVzcGlyYXRvcnkgc3luY3l0aWFsIHZpcnVzKSwgSUFWIChpbmZsdWVuemEgQSB2aXJ1cykgYW5kIEhQSVYzIChodW1hbiBwYXJhaW5mbHVlbnplIHZpcnVzIHR5cGUgMykgcHVibGlzaGVkIGluICpDZWxsKiBkb2k6IDEwLjEwMTYvai5jZWxsLjIwMjAuMDQuMDI2DQoNCiMjIyAwLjQuNC40IERhdGFzZXQgNDogQmxhbmNvLU1lbG8yMDIwX1N1cHBfRGF0YV80Lnhsc3gNCg0KVGhpcyBpcyBhbiBSTkFzZXEgZGF0YXNldCBjb21wYXJpc29uIGZvciBpbmZlY3Rpb24gb2YgaHVtYW4gYWx2ZW9sYXIgYWRlbm9jYXJjaW5vbWEgKEE1NDkpIGNlbGxzIHdpdGggYW5kIHdpdGhvdXQgaW5mbHVlbnphIEEgdmlydXMgKElBVikgcHVibGlzaGVkIG9uICpiaW9SeGl2KiBkb2k6IDxodHRwczovL2RvaS5vcmcvMTAuMTEwMS8yMDIwLjAzLjI0LjAwNDY1NT4NCg0KIyMjIDAuNC40LjUgRGF0YXNldCA1OiBMZWN0dXJlMDQuUkRhdGENCg0KQSBzYXZlZCBmaWxlIHdpdGggYSBmaW5hbCBHTyBhbm5vdGF0aW9uIGRhdGFzZXQgd2UgbG9vayBhdCB3aXRoIG91ciB2aXN1YWxpemF0aW9ucyBvZiBkb3QgcGxvdHMuDQoNCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KDQojIyAwLjUuMCBQYWNrYWdlcyB1c2VkIGluIHRoaXMgbGVzc29uDQoNCmB0aWR5dmVyc2VgIHdoaWNoIGhhcyBhIG51bWJlciBvZiBwYWNrYWdlcyBpbmNsdWRpbmcgYGRwbHlyYCwgYHRpZHlyYCwgYHN0cmluZ3JgLCBgZm9yY2F0c2AgYW5kIGBnZ3Bsb3QyYA0KDQpgdmlyaWRpc2AgaGVscHMgdG8gY3JlYXRlIGNvbG9yLWJsaW5kIHBhbGV0dGVzIGZvciBvdXIgZGF0YSB2aXN1YWxpemF0aW9ucw0KDQpgbmV0d29ya0QzYCwgYGdnaGlnaGxpZ2h0YCwgYGdncmVwZWxgLCBhbmQgYFVwU2V0UmAgYXJlIHVzZWQgaW4gYnVpbGRpbmcgc29tZSBvZiBvdXIgdmlzdWFsaXphdGlvbnMgaW5jbHVkaW5nIGZsb3djaGFydHMsIGFuZCB1cHNldCBwbG90cy4NCg0KYGdvc2VxYCwgYW5kIGBvcmcuSHMuZWcuZGJgIGFyZSBwYWNrYWdlcyB0aGF0IHdpbGwgaGVscCB1cyBwZXJmb3JtIHNvbWUgYmFzaWMgR2VuZSBPbnRvbG9neSAoR08pIGFubm90YXRpb25zIHdpdGggb3VyIGRhdGEuDQoNCmBgYHtyfQ0KIyAjIEluc3RhbGwgdGhlc2UgcGFja2FnZXMgb250byByLmRhdGF0b29scw0KIyBpbnN0YWxsLnBhY2thZ2VzKCJkZXZ0b29scyIpDQojIGluc3RhbGwucGFja2FnZXMoIm5ldHdvcmtEMyIpDQojIGluc3RhbGwucGFja2FnZXMoImdnaGlnaGxpZ2h0IikNCiMgaW5zdGFsbC5wYWNrYWdlcygiQ29tcGxleFVwc2V0IiwgdHlwZT0ic291cmNlIikNCiMgaW5zdGFsbC5wYWNrYWdlcygiR0dhbGx5IikNCiMgaW5zdGFsbC5wYWNrYWdlcygiZ2dyZXBlbCIpDQojIA0KIyANCiMgIyAyMjAzMzE6IEN1cnJlbnQgdmVyc2lvbiBvZiBKdXB5dGVySHViIGlzIG5vdCBhbGxvd2luZyBnb3NlcSB0byBiZSBpbnN0YWxsZWQgY29ycmVjdGx5DQojIA0KIyAjICMgU29tZSBwYWNrYWdlcyBjYW4gYmUgaW5zdGFsbGVkIHZpYSBCaW9jb25kdWN0b3INCiMgaWYgKCFyZXF1aXJlTmFtZXNwYWNlKCJCaW9jTWFuYWdlciIsIHF1aWV0bHkgPSBUUlVFKSkNCiMgICAgIGluc3RhbGwucGFja2FnZXMoIkJpb2NNYW5hZ2VyIikNCiMgQmlvY01hbmFnZXI6Omluc3RhbGwodmVyc2lvbiA9ICIzLjE4IikNCiMgDQojICMgVGhpcyBzaG91bGQgaW5zdGFsbCBnb3NlcSAxLjU0LjAuIENoZWNrIHRoaXMgdW5kZXIgdGhlIHBhY2thZ2VzIHRhYiENCiMgQmlvY01hbmFnZXI6Omluc3RhbGwoImdvc2VxIikNCiMgQmlvY01hbmFnZXI6Omluc3RhbGwoIm9yZy5Icy5lZy5kYiIpDQojIA0KIyAjIFRoaXMgbGFzdCBwYXJ0IHdpbGwgaW5zdGFsbCBzb21lIG9sZGVyIHZlcnNpb24gcGFja2FnZXMgc28gdGhhdCBDb21wbGV4VXBzZXQgd2lsbCBydW4gcHJvcGVybHkNCiMgZGV2dG9vbHM6Omluc3RhbGxfdmVyc2lvbigiZHBseXIiLCB2ZXJzaW9uID0gIjEuMS4zIiwgcmVwb3MgPSAiaHR0cDovL2NyYW4udXMuci1wcm9qZWN0Lm9yZyIpDQojIGRldnRvb2xzOjppbnN0YWxsX3ZlcnNpb24oImdncGxvdDIiLCB2ZXJzaW9uID0gIjMuNC40IiwgcmVwb3MgPSAiaHR0cDovL2NyYW4udXMuci1wcm9qZWN0Lm9yZyIpDQpgYGANCg0KLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQoNCldlJ3JlIHBsYXlpbmcgZmFzdCBhbmQgbG9vc2Ugd2l0aCBzb21lIGluc3RhbGxhdGlvbnMgaGVyZSB0byBnZXQgaXQgdG8gd29yayBvbiByLmRhdGF0b29scy51dG9yb250by5jYSBzbyBhZnRlciB0aGUgaW5zdGFsbGF0aW9uIGlzIGNvbXBsZXRlLCByZXN0YXJ0IHlvdXIgc2Vzc2lvbiB3aXRoIGBTZXNzaW9uID4gUmVzdGFydCBSYC4gRG9uJ3QgcmVmcmVzaCB5b3VyIGJyb3dzZXIgb3IgeW91J2xsIGxvc2UgdGhlIGluc3RhbGxhdGlvbi4NCg0KYGBge3J9DQojIFBhY2thZ2VzIHRvIGhlbHAgdGlkeSBvdXIgZGF0YQ0KbGlicmFyeSh0aWR5dmVyc2UpDQpsaWJyYXJ5KHJlYWR4bCkNCg0KIyBQYWNrYWdlcyBmb3IgdGhlIGdyYXBoaWNhbCBhbmFseXNpcyBzZWN0aW9uDQpsaWJyYXJ5KHZpcmlkaXMpDQoNCiMgTGVjdHVyZSAwNCB2aXN1YWxpemF0aW9uIHBhY2thZ2VzDQpsaWJyYXJ5KG5ldHdvcmtEMykNCmxpYnJhcnkoR0dhbGx5KQ0KbGlicmFyeShnZ2hpZ2hsaWdodCkNCmxpYnJhcnkoZ2dyZXBlbCkNCmxpYnJhcnkoQ29tcGxleFVwc2V0KQ0KYGBgDQoNCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KDQpMb2FkIHRoZXNlIGxpYnJhcmllcyBzZXBhcmF0ZWx5IHRvIHNhdmUgb24gYSBsaXR0bGUgYml0IG9mIG1lbW9yeSB1c2FnZS4gV2UnbGwgbmVlZCBldmVyeSBsYXN0IGRyb3Agd2UgY2FuIGdldC4NCg0KYGBge3J9DQojIEdPIHRlcm0gYW5hbHlzaXMgcGFja2FnZXMgaWYgeW91IG1hbmFnZWQgdG8gaW5zdGFsbCBnb3NlcQ0KbGlicmFyeShnb3NlcSkNCmxpYnJhcnkob3JnLkhzLmVnLmRiKQ0KYGBgDQoNCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KDQojIDEuMC4wIFdvcmtpbmcgd2l0aCBsYXJnZSBhbW91bnRzIG9mIGRhdGENCg0KVW50aWwgdGhpcyBwb2ludCwgdGhlIGRhdGEgc2V0cyB3ZSBoYXZlIHVzZWQgaW4gbGVjdHVyZSBoYXZlIGJlZW4gcmVsYXRpdmVseSBzaW1wbGUgZXBpZGVtaW9sb2dpY2FsIGluZm9ybWF0aW9uLiBBbGwgb2Ygb3VyIG9ic2VydmF0aW9ucyBhcmUgdmFsdWVzIGxpa2UgbmV3IGNhc2VzIG9yIHZhY2NpbmF0ZWQgaW5kaXZpZHVhbHMgdGllZCB0byBkYXRlcyBvciB0aW1lIHBlcmlvZHMuIFdoZXJlYXMgbW9zdCBvZiBvdXIgb2JzZXJ2YXRpb25zIHdlcmUgY29ubmVjdGVkIGluIHNvbWUgbGluZWFyIGZhc2hpb24sIHRoZSBkYXRhIHdlIGV4YW1pbmUgdG9kYXkgd2lsbCBoYXZlIHRlbnMgb2YgdGhvdXNhbmRzIG9mIG9ic2VydmF0aW9ucywgZWFjaCByZXByZXNlbnRpbmcgYSBkaWZmZXJlbnQgZ2VuZSENCg0KR2l2ZW4gdGhlIGNvbXBsZXhpdHkgb2Ygb3VyIGRhdGEsIHdlIHdpbGwgZGlzY3VzcyB3YXlzIHRvIHNvcnQsIHBhcnRpdGlvbiwgdmlzdWFsaXplIGFuZCBpbnRlcnByZXQgaXQuDQoNCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KDQojIyAxLjEuMCBXaGVyZSBkb2VzIFJOQXNlcSBkYXRhIGNvbWUgZnJvbT8NCg0KTWFueSBvZiB5b3UgbWF5IGJlIGZhbWlsaWFyIHdpdGggdGhlIGlkZWEgb2YgUk5Bc2VxIGRhdGEuIEl0IGJlZ2lucyBpbiB0aGUgcmVhbCB3b3JsZCAob3IgYXQgdGhlIGJlbmNoKSB3aXRoIGluZGl2aWR1YWxzL2dyb3Vwcy9jb25kaXRpb25zIGNvbXBhcmVkIGFnYWluc3QgYSBjb250cm9sIHN0YXRlLiBCaW9sb2dpY2FsIHNhbXBsZXMgY2FuIGJlIGNvbGxlY3RlZCBpbiB0aW1lIHNlcmllcyBvciBhdCBhIHNpbmdsZSBlbmRwb2ludC4gRWFjaCBleHBlcmltZW50YWwgY29uZGl0aW9uIHNob3VsZCBoYXZlIGl0J3MgbWF0Y2hpbmcgY29udHJvbCBvciBiYXNlbGluZSBwcm9maWxlIGZvciBjb21wYXJpc29uLCBub3QgdG8gbWVudGlvbiBiaW9sb2dpY2FsIHJlcGxpY2F0ZXMhDQoNCkFmdGVyIGNvbGxlY3RpbmcgeW91ciBzYW1wbGVzIHlvdSBzdGlsbCBuZWVkIHRvIHByZXBhcmUgYW5kIHNlcXVlbmNlIHlvdXIgbGlicmFyaWVzLCBjaGVjayB5b3VyIGRhdGEgcXVhbGl0eSwgdHJpbSByZWFkcywgbWFwIHRoZW0gdG8gYSB0cmFuc2NyaXB0b21lLCBlc3RpbWF0ZS9ub3JtYWxpemUgdGhlIGV4cHJlc3Npb24gb2YgZ2VuZXMgd2l0aGluIGVhY2ggbGlicmFyeSBhbmQgKnRoZW4qIGNvbXBhcmUgdGhlIGV4cHJlc3Npb24gYmV0d2VlbiBsaWJyYXJpZXMvc2FtcGxlcyB0byBkZXRlcm1pbmUgaWYgdGhlcmUgYXJlIHRyYW5zY3JpcHRpb25hbCBkaWZmZXJlbmNlcyEgQWxsIG9mIHRoZXNlIHN0ZXBzIGFyZSBiZXlvbmQgdGhpcyBjbGFzcyBCVVQgaWYgeW91IHBsYW4gb24gd29ya2luZyB3aXRoIHRyYW5zY3JpcHRvbWVzLCB5b3UnbGwgZXZlbnR1YWxseSBlbmNvdW50ZXIgdGhpcyBwcm9jZXNzLiBUb2RheSB3ZSdsbCBqdW1wIHRoZSBsaW5lIGFuZCB3b3JrIGRpcmVjdGx5IHdpdGggZGlmZmVyZW50aWFsIGV4cHJlc3Npb24gKERFKSBkYXRhLiBUaGF0IG1lYW5zIHdlJ2xsIGJlIGF0IHRoZSBib3R0b20gcmlnaHQgY29ybmVyIG9mIHRoZSBmb2xsb3dpbmcgZGlhZ3JhbSBBS0EsIHRoZSBlbmQgb2YgdGhlIHBpcGVsaW5lLg0KDQo6Ojoge2FsaWduPSJjZW50ZXIifQ0KPGltZyBzcmM9Imh0dHBzOi8vZ2l0aHViLmNvbS9jYW1vay9DU0JfQ291cnNlX01hdGVyaWFscy9ibG9iL21haW4vQWR2Vml6L1JOQXNlcV8xLnBuZz9yYXc9dHJ1ZSIgd2lkdGg9IjkwMCIvPg0KDQpJbWFnZSBmcm9tIEJpb2luZm9ybWF0aWNzIFdvcmtib29rOiBbUk5BIFNlcXVlbmNpbmcgQW5hbHlzaXNdKGh0dHBzOi8vYmlvaW5mb3JtYXRpY3N3b3JrYm9vay5vcmcvZGF0YUFuYWx5c2lzL1JOQS1TZXEvUk5BLVNlcUludHJvL1JOQXNlcS11c2luZy1hLWdlbm9tZS5odG1sI2dzYy50YWI9MCkNCjo6Og0KDQotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCg0KIyMgMS4yLjAgVXNlIGEgU2Fua2V5IGRpYWdyYW0gdG8gaWxsdXN0cmF0ZSB5b3VyIHdvcmtmbG93DQoNClNhbmtleSBkaWFncmFtcyBhcmUgbmFtZWQgYWZ0ZXIgSXJpc2ggQ2FwdGFpbiBNYXR0aGV3IEhlbnJ5IFBoaW5lYXMgUmlhbGwgU2Fua2V5LCB3aG8gZmlyc3QgcHJlc2VudGVkIHRoaXMgdHlwZSBvZiB2aXN1YWxpemF0aW9uIGluIDE4OTggdG8gY29udmV5IHRoZSBlbmVyZ3kgZWZmaWNpZW5jeSBvZiBhIHN0ZWFtIGVuZ2luZSENCg0KV2l0aCBsYXJnZXIgc2V0cyBvZiBkYXRhIGFjcm9zcyBtdWx0aXBsZSBleHBlcmltZW50cywgaXQgY2FuIHNvbWV0aW1lcyBiZSBoZWxwZnVsIHRvIHVzZSBhIFNhbmtleSBkaWFncmFtIHRvIGV4cGxhaW4gdGhlIGNvbm5lY3Rpb24gYmV0d2VlbiBkaWZmZXJlbnQgYXNwZWN0cyBvZiB5b3VyIHNhbXBsZXMuIFRoZSBrZXkgYXR0cmlidXRlIG9mIHRoZSBTYW5rZXkgZGlhZ3JhbSBpcyB0aGF0IHRoZSB3aWR0aCBvZiBhIGNvbm5lY3Rpb24gKGZsb3cpIGlzIHByb3BvcnRpb25hbCB0byB0aGUgcXVhbnRpdHkgcmVwcmVzZW50ZWQuIFNvIHRoZSBTYW5rZXkgZGlhZ3JhbSBpcyBhIGZsb3cgZGlhZ3JhbSB0aGF0IGFsc28gdmlzdWFsbHkgcXVhbnRpZmllcyB0aGUgZHluYW1pY3Mgb2YgdGhlIHJlbGF0aW9uc2hpcHMgaW4geW91ciBkaWFncmFtLiBUaGluayBvZiBpdCBtdWNoIGxpa2UgYSBzdGFja2VkIGJhciBjaGFydCB0aGF0IGNhbiBzcGxpdCBvciBqb2luIHdpdGggb3RoZXIgYmFycyBhdCBlYWNoIGNvbm5lY3Rpb24uDQoNCkEgdXNlZnVsIHBhY2thZ2UgdG8gZ2VuZXJhdGUgU2Fua2V5IGRpYWdyYW1zIGlzIGBuZXR3b3JrRDNgIHVzaW5nIHRoZSBgc2Fua2V5TmV0d29yaygpYCBmdW5jdGlvbi4gVG8gZ2VuZXJhdGUgYSBTYW5rZXkgZGlhZ3JhbSwgeW91IG5lZWQgaW5mb3JtYXRpb24gb24gdHdvIHZhcmlhYmxlcyB3aXRoIGEgdGhpcmQgb3B0aW9uYWwgb25lOg0KDQotICAgYHNvdXJjZWA6IGEgc3RhcnRpbmcgcG9pbnQgb3IgZm9yIHlvdXIgZmxvdw0KLSAgIGB0YXJnZXRgOiB0aGUgZW5kIHBvaW50IG9mIHlvdXIgZmxvdw0KLSAgIGB2YWx1ZWA6IHRoZSBudW1iZXIgdGhhdCB3aWxsIHJlcHJlc2VudCB0aGUgd2lkdGggb2YgeW91ciBmbG93LiBVc3VhbGx5IHNvbWUgcXVhbnRpdHkgb2YgY29udHJpYnV0aW9uIGJ1dCB0aGlzIGNhbiBhbHNvIGJlIGFuIG9wdGlvbmFsIHZhcmlhYmxlLg0KDQo6Ojoge2FsaWduPSJjZW50ZXIifQ0KPGltZyBzcmM9Imh0dHBzOi8vZ2l0aHViLmNvbS9jYW1vay9DU0JfQ291cnNlX01hdGVyaWFscy9ibG9iL21haW4vQWR2Vml6L3NhbmtleXBsb3QucG5nP3Jhdz10cnVlIiB3aWR0aD0iOTAwIi8+DQoNCldlJ2xsIHRha2UgdGhlIHRpbWUgdG8gcmVjb25zdHJ1Y3QgdGhpcyBzYW5rZXkgcGxvdCBpbiB0aGUgZm9sbG93aW5nIHNlY3Rpb25zLg0KOjo6DQoNCldlJ2xsIGZpbmQgYSBwcmUtbWFkZSB0YWJsZSB0aGF0IGRlc2NyaWJlcyBhIHRoZW9yZXRpY2FsIFJOQXNlcSB3b3JrZmxvdyBiYXNlZCBpbiB0aGUgZGF0YXNldHMgd2UnbGwgYmUgd29ya2luZyB3aXRoIHRvZGF5LiBTb21lIHNlY3Rpb25zIGhhdmUgYmVlbiBjb21wcmVzc2VkIGZvciBicmV2aXR5IGJ1dCB3ZSdsbCBmaW5kIGFsbCBvZiB0aGF0IGluZm9ybWF0aW9uIGluIGAuL2RhdGEvTGVjdHVyZTA0X3NhbmtleV9kYXRhLmNzdmAuIExldCdzIGJlZ2luIGJ5IHJlYWRpbmcgaXQgaW4uDQoNCmBgYHtyfQ0KIyBSZWFkIGluIG91ciBTYW5rZXkgZGlhZ3JhbSBpbiAuL2RhdGEvTGVjdHVyZTA0X3NhbmtleV9kYXRhLmNzdiANCg0Kc2Fua2V5X2luZm8uZGYgPC0gcmVhZF9jc3YoImRhdGEvTGVjdHVyZTA0X3NhbmtleV9kYXRhLmNzdiIpDQoNCiMgQ2hlY2sgdGhlIHN0cnVjdHVyZQ0Kc3RyKHNhbmtleV9pbmZvLmRmKQ0KDQojIExvb2sgYSBmZXcgcm93cw0KaGVhZChzYW5rZXlfaW5mby5kZikNCmBgYA0KDQotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCg0KRnJvbSB0aGUgYWJvdmUgb3V0cHV0IHdlIGNhbiBzZWUgdGhhdCBvbmUgYHNvdXJjZWAgbWF5IGhhdmUgbXVsdGlwbGUgYHRhcmdldGAgdmFsdWVzIGFuZCB2aWNlIHZlcnNhLg0KDQojIyMgMS4yLjEgQ29udmVydCB5b3VyIGZsb3cgZGF0YSBmcmFtZSBpbnRvIGEgbm9kZSBkYXRhIGZyYW1lDQoNCkFsdGhvdWdoIG91ciBkYXRhIGZyYW1lIGBzYW5rZXlfaW5mby5kZmAgY29udGFpbnMgdGhlIGluZm9ybWF0aW9uIGFib3V0IG91ciBjb25uZWN0aW9ucywgdGhlIGBuZXR3b3JrRDNgIHBhY2thZ2UgcmVxdWlyZXMgc29tZSBtYXBwaW5nIG9mIHRoZSBub2RlcyB3aXRoaW4gb3VyIGRhdGEuIEVhY2ggdW5pcXVlIHNvdXJjZSBhbmQgdGFyZ2V0IHdpbGwgYmUgY291bnRlZCBhcyBhIG5vZGUgYW5kIHdlJ2xsIHNhdmUgdGhhdCBpbmZvcm1hdGlvbiBpbnRvIGBzYW5rZXlfbm9kZXMuZGZgLg0KDQpgYGB7cn0NCiMgQ29tYmluZSBvdXIgc291cmNlIGFuZCB0YXJnZXQgbGlzdHMsIHRoZW4gdGFrZSB0aGUgdW5pcXVlIHNldCBhbmQgcHV0IHRoYXQgaW50byB0aGUgIm5hbWUiIHZlY3Rvci4NCg0KIyBzYW5rZXlfbm9kZXMuZGYgPC0gZGF0YS5mcmFtZShuYW1lPWMoYXMuY2hhcmFjdGVyKHNhbmtleV9pbmZvLmRmJHNvdXJjZSksICAgICAgIyBMb2NhdGlvbiBvZiBvdXIgc291cmNlIG5vZGVzDQojICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBhcy5jaGFyYWN0ZXIoc2Fua2V5X2luZm8uZGYkdGFyZ2V0KSkgJT4lICAjIExvY2F0aW9uIG9mIG91ciB0YXJnZXQgbm9kZXMNCiMgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdW5pcXVlKCkgIyBHZW5lcmF0ZSB0aGUgdW5pcXVlIGNvbWJpbmF0aW9uIG9mIHRoZSB0d28gc2V0cw0KIyAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICkNCg0KIyBDb21iaW5lIG91ciB0YXJnZXQgYW5kIHNvdXJjZSBub2RlcyBpbnRvIGEgc2luZ2xlIHZlY3Rvcg0Kc2Fua2V5X25vZGVzLmRmIDwtIGMoc2Fua2V5X2luZm8uZGYkc291cmNlLA0KICAgICAgICAgICAgICAgICAgICAgYXMuY2hhcmFjdGVyKHNhbmtleV9pbmZvLmRmJHRhcmdldCkpICU+JQ0KICANCiAgIyBPbmx5IGtlZXAgdGhlIHVuaXF1ZSB2YWx1ZXMNCiAgdW5pcXVlKCkgJT4lDQogIA0KICAjIFB1dCB0aGlzIGludG8gYSBkYXRhIGZyYW1lIHVuZGVyIGEgY29sdW1uIGNhbGxlZCAibmFtZSINCiAgZGF0YS5mcmFtZShuYW1lID0gLikNCg0KDQojIExvb2sgYXQgdGhlIHN0cnVjdHVyZSwgaXQncyBqdXN0IGEgbGlzdCBvZiB0aGUgbm9kZXMNCnN0cihzYW5rZXlfbm9kZXMuZGYpDQpgYGANCg0KLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQoNCiMjIyAxLjIuMiBNYXAgeW91ciBub2RlIG5hbWVzIGJhY2sgdG8geW91ciBzb3VyY2UgYW5kIHRhcmdldCBkYXRhIHdpdGggYG1hdGNoKClgDQoNCk5vdyB0aGF0IHdlIGhhdmUgb3VyIG5vZGUgaW5mb3JtYXRpb24gKDE4IHRvdGFsIHVuaXF1ZSBub2Rlcykgc3RvcmVkIGJhc2ljYWxseSBhcyBhbiBpZGVudGlmaWNhdGlvbiBudW1iZXIsIHdlIGhhdmUgdG8gbWFwIGl0IGJhY2sgdG8gYHNhbmtleV9pbmZvLmRmYC4gVGhpcyBpcyBlc3NlbnRpYWxseSBsaWtlIHdvcmtpbmcgd2l0aCBhIGZhY3RvciB0aGF0IHNwYW5zIHR3byBjb2x1bW5zIGluc3RlYWQgb2YganVzdCBvbmUuIFRvIGFjY29tcGxpc2ggdGhlIG1hcHBpbmcsIHdlJ2xsIHVzZSB0aGUgYG1hdGNoKClgIG1ldGhvZC4gVGhlIGBtYXRjaCgpYCBmdW5jdGlvbiB0YWtlcyB0d28gdmFsdWVzOg0KDQotICAgYHhgOiB0aGUgdmFsdWVzIHRvIGJlIG1hdGNoZWQNCg0KLSAgIGB0YWJsZWA6IHRoZSB2YWx1ZXMgdG8gYmUgbWF0Y2hlZCBhZ2FpbnN0DQoNCmFuZCByZXR1cm5zIGEgdmVjdG9yIG9mIGxlbmd0aCBgeGAgd2hlcmUgZWFjaCBpbnRlZ2VyIHZhbHVlIHJlcHJlc2VudHMgdGhlIHBvc2l0aW9uIG9mIGl0cyBtYXRjaGVkIHZhbHVlIGZyb20gYHRhYmxlYC4gVW5tYXRjaGVkIHZhbHVlcyBmcm9tIGB4YCAoaWUgbm90IGZvdW5kIGluIGB0YWJsZWApIGFyZSBzZXQgdG8gYG5vbWF0Y2hgLg0KDQo6Ojogey5hbGVydCAuYWxlcnQtYmxvY2sgLmFsZXJ0LWluZm99DQoqKm1hdGNoKCkgdnMgIiVpbiUiKio6IFRoZSAqKm1hdGNoKCkqKiBmdW5jdGlvbiBhbmQgdGhlIG1hdGNoaW5nIG9wZXJhdG9yICoqJWluJSoqIGFyZSB2ZXJ5IHNpbWlsYXIgaW4gaWRlYS4gV2hlcmVhcyB0aGUgZm9ybWVyIHByb2R1Y2VkIGEgc2V0IG9mIG1hdGNoZWQgcG9zaXRpb25zIGJldHdlZW4gc2V0cywgdGhlIGxhdHRlciBwcm9kdWNlcyBhIGxvZ2ljYWwgdmVjdG9yIGluZGljYXRpbmcgaW5jbHVzaW9uIChUUlVFKSBvciBleGNsdXNpb24gKEZBTFNFKSBmcm9tIHRoZSBpbnRlcnNlY3Qgb2YgdGhlIHNldHMuDQo6OjoNCg0KTGV0J3MgdHJ5IHdpdGggYSBxdWljayBleGFtcGxlIHdpdGggb3VyIGRhdGENCg0KYGBge3J9DQojIFdoaWNoIG5vZGVzIG1hdGNoIGZyb20gb3VyIHNvdXJjZSBub2RlcyB0byBvdXIgY29sbGVjdGVkIG5vZGVzICgxOCB0b3RhbCBub2Rlcyk/DQptYXRjaChzYW5rZXlfaW5mby5kZiRzb3VyY2UsIHNhbmtleV9ub2Rlcy5kZiRuYW1lKQ0KYGBgDQoNCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KDQpOb3RlIHRoYXQgb3VyIG5vZGUgaW5mb3JtYXRpb24gbXVzdCBiZSAwLWluZGV4ZWQhIFIgdXNlcyBhbiBpbmRleGluZyBzY2hlbWUgdGhhdCBiZWdpbnMgYXQgMSBzbyB3ZSdsbCBuZWVkIHRvIG9mZnNldCB0aGF0IGluZm9ybWF0aW9uIHRoYXQgd2UgZ2VuZXJhdGUgYXMgd2VsbC4NCg0KYGBge3J9DQojIEFkZCBvdXIgc2Fua2V5X25vZGVzLmRmIGFzIGluZGV4IGluZm9ybWF0aW9uIHRoYXQgbWFwcyBiZXR3ZWVuIGl0IGFuZCBgc2Fua2V5X2luZm8uZGZgDQpzYW5rZXlfaW5mby5kZiA8LQ0Kc2Fua2V5X2luZm8uZGYgICU+JSBtdXRhdGUoSURzb3VyY2UgPSBtYXRjaCguJHNvdXJjZSwgc2Fua2V5X25vZGVzLmRmJG5hbWUpIC0xLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgSUR0YXJnZXQgPSBtYXRjaCguJHRhcmdldCwgc2Fua2V5X25vZGVzLmRmJG5hbWUpIC0xKSAjIG5vZGVzIG11c3QgYmUgMC1pbmRleGVkDQoNCiMgdGFrZSBhIHBlZWsgYXQgd2hhdCB3ZSd2ZSB3cm91Z2h0DQpoZWFkKHNhbmtleV9pbmZvLmRmKQ0KYGBgDQoNCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KDQojIyMgMS4yLjMgQnVpbGQgeW91ciBTYW5rZXkgZGlhZ3JhbSB3aXRoIGBzYW5rZXlOZXR3b3JrKClgDQoNCk5vdyB0aGF0IHdlIGhhdmUgb3VyIHR3byBuZWNlc3NhcnkgZGF0YSBmcmFtZXMsIHdlIGNhbiBidWlsZCBvdXIgU2Fua2V5IGRpYWdyYW0gd2l0aCB0aGUgYHNhbmtleU5ldHdvcmsoKWAgZnVuY3Rpb24uIFRoaXMgZnVuY3Rpb24gaGFzIGEgZmV3IHBhcmFtZXRlcnMgdGhhdCB3ZSdsbCB3YW50IHRvIHNldDoNCg0KLSAgIGBMaW5rc2A6IHRoZSBsb2NhdGlvbiBvZiBvdXIgc291cmNlL3RhcmdldCByZWxhdGlvbnNoaXAgaW5mb3JtYXRpb24gKGBzYW5rZXlfaW5mby5kZmApLg0KDQotICAgYE5vZGVzYDogdGhlIGxpc3Rpbmcgb2Ygbm9kZXMgaW4gb3VyIGRpYWdyYW0gKGBzYW5rZXlfbm9kZXMuZGZgKS4NCg0KLSAgIGBTb3VyY2VgOiB0aGUgbmFtZSBvZiB0aGUgc291cmNlIGNvbHVtbiAoYElEc291cmNlYCkuDQoNCi0gICBgVGFyZ2V0YDogdGhlIG5hbWUgb2Ygb3VyIHRhcmdldCBjb2x1bW4gKGBJRHRhcmdldGApLg0KDQotICAgYFZhbHVlYDogdGhlIG5hbWUgb2Ygb3VyICJvcHRpb25hbCIgdmFsdWUgdG8gYnVpbGQgdGhlIHdpZHRoIG9mIGZsb3dzIChgdmFsdWVgKS4NCg0KLSAgIGBOb2RlSURgOiB0aGUgbmFtZXMgb2Ygb3VyIG5vZGVzIChgc2Fua2V5X25vZGVzLmRmJG5hbWVgKS4NCg0KVGhlcmUgYXJlIGFuIGFkZGl0aW9uYWwgbnVtYmVyIG9mIHBhcmFtZXRlcnMgdGhhdCBhbGxvdyB5b3UgdG8gcGxheSB3aXRoIHRoZSB2aXN1YWxpemF0aW9uIG9mIHRoaXMgZGlhZ3JhbSBpbmNsdWRpbmc6DQoNCi0gICBgY29sb3VyU2NhbGVgOiBjYXRlZ29yaWNhbCBjb2xvdXIgb2YgeW91ciBub2RlLiBXb3JrcyB0b2dldGhlciB3aXRoLi4uDQoNCi0gICBgTm9kZUdyb3VwYDogZm9yIGNvbG91cmluZyB5b3VyIG5vZGVzIGJhc2VkIG9uIGFuIGV4dHJhIGNvbHVtbiBpbiBgTm9kZXNgLg0KDQotICAgYExpbmtHcm91cGA6IGZvciBjb2xvdXJpbmcgeW91ciBsaW5rcyBiYXNlZCBvbiBhbiBleHRyYSBjb2x1bW4gaW4gYExpbmtzYC4NCg0KLSAgIGBub2RlV2lkdGhgOiB0aGUgbnVtZXJpYyB3aWR0aCBvZiBlYWNoIG5vZGUuDQoNCkZvciBtb3JlIGluZm9ybWF0aW9uIG9uIHRoZXNlIHBhcmFtZXRlcnMgYW5kIG90aGVycywgeW91IGNhbiBnbyBbaGVyZV0oaHR0cHM6Ly93d3cucmRvY3VtZW50YXRpb24ub3JnL3BhY2thZ2VzL25ldHdvcmtEMy92ZXJzaW9ucy8wLjQvdG9waWNzL3NhbmtleU5ldHdvcmspDQoNCmBgYHtyLCBmaWcud2lkdGggPSAxMiwgZmlnLmhlaWdodD02fQ0KIyBCdWlsZCB0aGUgU2Fua2V5IGRpYWdyYW0gd2l0aCBhbGwgb2Ygb3VyIGluZm9ybWF0aW9uDQoNCnNhbmtleU5ldHdvcmsoTGlua3MgPSBzYW5rZXlfaW5mby5kZiwgIyBXaGVyZSBpcyB0aGUgImVkZ2UiIGluZm9ybWF0aW9uDQogICAgICAgICAgICAgIE5vZGVzID0gc2Fua2V5X25vZGVzLmRmLCAjIFdoZXJlIGlzIHRoZSAibm9kZSIgaW5mb3JtYXRpb24NCiAgICAgICAgICAgICAgU291cmNlID0gIklEc291cmNlIiwgIyBXaGVyZSBhcmUgdGhlIHNvdXJjZSBub2RlIGxhYmVscw0KICAgICAgICAgICAgICBUYXJnZXQgPSAiSUR0YXJnZXQiLCAjIFdoZXJlIGFyZSB0aGUgdGFyZ2V0IG5vZGUgbGFiZWxzDQogICAgICAgICAgICAgIFZhbHVlID0gInZhbHVlIiwgIyBXaGF0IHZhbHVlIGRvIHdlIGFzc2lnbiB0byB0aGUgcmVsYXRpb25zaGlwcy9lZGdlcw0KICAgICAgICAgICAgICBOb2RlSUQgPSAibmFtZSIsICMgV2hlcmUgYXJlIHRoZSBsYWJlbHMgZm9yIHRoZSBub2RlcyBzdG9yZWQ/DQogICAgICAgICAgICAgIHNpbmtzUmlnaHQgPSBUUlVFLCAjIFB1c2ggdGhlIG5vZGVzIHRvIHRoZSByaWdodA0KICAgICAgICAgICAgICBmb250U2l6ZSA9IDEwKQ0KYGBgDQoNCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KDQojIyAxLjMuMCBTY2F0dGVycGxvdCBtYXRyaWNlcyBmb3Igc29tZSBxdWljayBRQSBvZiB5b3VyIHJlYWQgY291bnRzDQoNClN0ZXBwaW5nIGJhY2sgb25lIHN0ZXAgaW4gb3VyIGZsb3djaGFydCwganVzdCBwcmlvciB0byB5b3VyIERFIGFuYWx5c2lzLCB5b3UnbGwgaGF2ZSBhIHNldCBvZiByZWFkIGNvdW50cyBnZW5lcmF0ZWQgZnJvbSBSTkEtU2VxIGRhdGEuIFRoZSBjb3VudHMgYXJlIGFuIGVzdGltYXRpb24gb2YgZWFjaCB0cmFuc2NyaXB0IHdpdGhpbiB5b3VyIGRhdGEuIFNvbWUgcHJvZ3JhbXMgbWF5IGluY2x1ZGUgdGhlIGFuYWx5c2lzIG9mIHNwbGljZWQgaXNvZm9ybXMgYW5kIG90aGVycyBtYXkgbm90Lg0KDQpPZnRlbiBpbiB5b3VyIFJOQS1TZXEgZGF0YXNldHMsIHlvdSBzaG91bGQgaGF2ZSBtdWx0aXBsZSByZXBsaWNhdGVzIG9mIHlvdXIgZGF0YS4gQSBxdWljayB3YXkgdG8gYXNzZXNzIHRoZSBxdWFsaXR5IG9mIHlvdXIgZGF0YXNldHMgaXMgdG8gZ2VuZXJhdGUgYSBzY2F0dGVycGxvdCBtYXRyaXguIFRoZSBzY2F0dGVycGxvdCBtYXRyaXggaXMgYW4gYWxsLWJ5LWFsbCBhc3Nlc3NtZW50IG9mIHlvdXIgZXhwZXJpbWVudHMgdGhhdCBnZW5lcmF0ZXMgYSBzY2F0dGVycGxvdCBvZiByZWFkIGNvdW50cyBmb3IgZWFjaCBwYWlyLXdpc2UgZGF0YXNldC4NCg0KOjo6IHthbGlnbj0iY2VudGVyIn0NCjxpbWcgc3JjPSJodHRwczovL2dpdGh1Yi5jb20vY2Ftb2svQ1NCX0NvdXJzZV9NYXRlcmlhbHMvYmxvYi9tYWluL0FkdlZpei8xMjg1OV8yMDE5XzI5NjhfRmlnM19IVE1MLndlYnA/cmF3PXRydWUiIHdpZHRoPSI4MDAiLz4NCg0KQSBzY2F0dGVycGxvdCBtYXRyaXggY2FuIGhlbHAgdG8gcXVpY2tseSBhc3Nlc3MgdGhlIHF1YWxpdHkgYW5kIGNvbnNpc3RlbmN5IG9mIHlvdXIgZGF0YS4NCg0KRmlndXJlIHRha2VuIGZyb206IFZpc3VhbGl6YXRpb24gbWV0aG9kcyBmb3IgZGlmZmVyZW50aWFsIGV4cHJlc3Npb24gYW5hbHlzaXMuICpSdXR0ZXIgZXQgYWwuLCAyMDE5LiBCTUMgQmlvaW5mb3JtYXRpY3MgMjA6IDQ1OCoNCjo6Og0KDQotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCg0KIyMjIDEuMy4xIEltcG9ydCB5b3VyIHdpZGUtZm9ybWF0IHJlYWQgY291bnQgZGF0YS4NCg0KV2UnbGwgYmVnaW4gYnkgaW1wb3J0aW5nIG91ciBkYXRhIGZyb20gYFd5bGVyMjAyMF9BRUNfU0FSU0NvVjJfMTdBQUdfcmVhZGNvdW50cy50c3ZgLiBUaGlzIHRhYi1zZXBhcmF0ZWQgZGF0YSBmaWxlIGNvbnRhaW5zIHRvdGFsIFJOQSBleHBlcmltZW50cyBmcm9tIHRoZSBpbmZlY3Rpb24gb2YgYWlyd2F5IGVwaXRoZWxpYWwgY2VsbHMgKEFFQ3MpIHVuZGVyIGRpZmZlcmVudCBjb25kaXRpb25zLCB0cmVhdG1lbnRzIHdpdGggaW5oaWJpdG9ycywgYW5kIHRpbWVwb2ludHMuDQoNCmBgYHtyfQ0KIyBSZWFkIGluIHlvdXIgcmVhZF9jb3VudCBkYXRhDQp3eWxlcl9yZWFkY291bnRzLmRmIDwtIHJlYWRfdHN2KCIuL2RhdGEvV3lsZXIyMDIwX0FFQ19TQVJTQ29WMl8xN0FBR19yZWFkY291bnRzLnRzdiIpDQoNCnN0cih3eWxlcl9yZWFkY291bnRzLmRmLCBnaXZlLmF0dHIgPSBGQUxTRSkNCmBgYA0KDQotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCg0KIyMjIDEuMy4yIFByZXBhcmUgeW91ciBkYXRhIGFzIGEgY2xlYW4gcmVhZCBjb3VudCBtYXRyaXgNCg0KQmVmb3JlIHdlIHByb2NlZWQgdG8gZ2VuZXJhdGluZyBvdXIgc2NhdHRlcnBsb3QgbWF0cml4LCB3ZSBuZWVkIHRvIHNlbGVjdCBvdXIgZGF0YSBmcm9tIG91ciBtYWluIGRhdGFzZXQuIEluIHBhcnRpY3VsYXIsIHRoZSBkYXRhIGZyb20gKld5bGVyIGV0IGFsLiosIHRoYXQgd2UgaW1wb3J0ZWQgYXMgYHd5bGVyX3JlYWRjb3VudHMuZGZgIGNvbnRhaW5zIGRhdGEgZnJvbSBpbmZlY3RpbmcgaHVtYW4gYWlyd2F5IGVwaXRoZWxpYWwgY2VsbHMgKEFFQ3MpIGFuZCB0cmVhdGluZyB3aXRoIGVpdGhlciAqKkRNU08qKiBvciAyMDBuTSBvZiB0aGUgSFNQOTAgaW5oaWJpdG9yLCAqKjE3LUFBRyoqLiBXaXRoIGVhY2ggY29uZGl0aW9uIHRoZXJlIGlzIGEgc2V0IG9mIGluZmVjdGVkICgqKlMyKiopIG9yIHVuaW5mZWN0ZWQgQUVDcy4gRm9yIGVhY2ggb2YgdGhlc2UgNCBjb25kaXRpb25zLCB0aGVyZSBhcmUgMyByZXBsaWNhdGVzIGFuZCAzIHRpbWVwb2ludHMgKCoqMjQtKiosICoqNDgtKiosIGFuZCAqKjcyLSoqaG91cnMpLiBJbiB0b3RhbCB0aGF0IGFtb3VudHMgdG8gMzYgc2V0cyBvZiBkYXRhLg0KDQpGb3Igb3VyIHB1cnBvc2VzLCB3ZSdsbDoNCg0KMS4gIENvbXBhcmUgMjRoIHZzIDcyaCBpbmZlY3RlZCAoaWUgUzIgPSBTQVJTLUNvVjIpIERNU08tdHJlYXRlZCBmb3VuZCBpbiBjb2x1bW5zIDktMTEgYW5kIDMzLTM1LiBXZSBjYW4gZWl0aGVyIHNlbGVjdCBieSBpbmRleCBvciB1c2UgYSBzZWxlY3Rpb24gaGVscGVyIGxpa2UgYG1hdGNoZXMoKWAgdG8gaW5jb3Jwb3JhdGUgYSByZWd1bGFyIGV4cHJlc3Npb24uDQoNCjIuICBGaWx0ZXIgb3VyIHJlYWRzIHRvIHRoZSByYW5nZSBvZiAxMC01MDAwIHVzaW5nIHRoZSBgaWZfYWxsKClgIGZ1bmN0aW9uLg0KDQozLiAgUmVuYW1lIG91ciBjb2x1bW5zIHRvIHJlbW92ZSBzb21lIHJlZHVuZGFudCBkYXRhIChgQUVDSUlfeHhfYCkNCg0KTm90ZSB0aGF0IHRoZSBgaWZfYWxsKClgIGZ1bmN0aW9uIGlzIGEgcmF0aGVyIG5ldyBhZGRpdGlvbiB0byB0aGUgdGlkeXZlcnNlIGFuZCBpcyB1c2VkIHRvIGhlbHAgZmlsdGVyIG9uIGEgcHJlZGljYXRlIGFjcm9zcyAqbXVsdGlwbGUqIGNvbHVtbnMuIFRoaXMgaXMgdmVyeSBtdWNoIGxpa2UgdGhlIGBhY3Jvc3MoKWAgaGVscGVyIGZ1bmN0aW9uIHlvdSBtYXkgaGF2ZSB1c2VkIHByZXZpb3VzbHkgZm9yIHN1bW1hcml6aW5nIGRhdGEuIExpa2UgdGhlIGBhY3Jvc3MoKWAgZnVuY3Rpb24sIHRoZSBgaWZfYWxsKClgIGhlbHBlciBmdW5jdGlvbiB1c2VzIHRoZSBmb2xsb3dpbmcgcGFyYW1ldGVyczoNCg0KLSAgIGAuY29sc2A6IHRoZSBjb2x1bW5zIHlvdSB3aXNoIHRvIGFwcGx5IHlvdXIgZmlsdGVyaW5nIHByZWRpY2F0ZSB1cG9uLg0KDQotICAgYC5mbnNgOiB0aGUgZnVuY3Rpb24gb3IgcHJlZGljYXRlIHlvdSBhcmUgZmlsdGVyaW5nIHdpdGguDQoNCllvdSdsbCBub3RpY2Ugb3VyIHVzZSBvZiBgfmAgYWdhaW4gdG8gYW5vbnltaXplIHRoZSBmdW5jdGlvbi4NCg0KYGBge3J9DQpyZWFkY291bnRzXzI0aHY3MmggPC0NCiAgICB3eWxlcl9yZWFkY291bnRzLmRmICU+JSANCg0KICAgICMgU2VsZWN0IGZvciBqdXN0IHRoZSBjb2x1bW5zIHdpdGhzIFNBUlMtQ29WLTIgaW5mZWN0aW9uIGFuZCBETVNPIGF0IDI0IGFuZCA3MmgNCiAgICBkcGx5cjo6c2VsZWN0KG1hdGNoZXMociIoKDI0aHw3MmgpX1MyX0RNU08pIikpICU+JSANCg0KICAgICMgUmVzdHJpY3Qgb3VyIGRhdGEgYW5hbHlzaXMgdG8ganVzdCByZWFkY291bnRzIGFib3ZlIDEwIGFuZCBiZWxvdyA1MDAwDQogICAgZmlsdGVyKGlmX2FsbCguY29scyA9IGV2ZXJ5dGhpbmcoKSwgLmZucyA9IH4gLnggPjEwICYgLnggPCA1MDAwKSkgJT4lIA0KDQogICAgIyBSZW5hbWUgdGhlIGNvbHVtbnMgYnkgcmVtb3ZpbmcgdGhlIGZpcnN0IHBvcnRpb246IEFFQ0lJX3h4DQogICAgcmVuYW1lX3dpdGgoLiwgfiBzdHJfcmVwbGFjZShzdHJpbmcgPSAueCwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBwYXR0ZXJuID0gciIoXHcqX1xkKl8pIiwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICByZXBsYWNlID0gIiIpKQ0KDQojIFRha2UgYSBwZWVrIGF0IG91ciByZXN1bHRpbmcgdGliYmxlDQpzdHIocmVhZGNvdW50c18yNGh2NzJoKQ0KYGBgDQoNCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KDQojIyMgMS4zLjMgVXNlIGBnZ3BhaXJzKClgIHRvIGdlbmVyYXRlIGEgc2NhdHRlcnBsb3QgbWF0cml4DQoNCllvdSBtYXkgcmVjYWxsIG91ciB3b3JraW5nIHdpdGggdGhlIHBhcmFsbGVsIGNvb3JkaW5hdGUgcGxvdCBmcm9tICoqbGVjdHVyZSAyKiouIFJhdGhlciB0aGFuIHRyYW5zZm9ybSB0aGUgZGF0YSBvdXJzZWx2ZXMsIHdlIHBhc3NlZCBvdXIgZGF0YSBhbG9uZyB0byB0aGUgYGdncGFyY29vcmQoKWAgZnVuY3Rpb24gZnJvbSB0aGUgYEdHYWxseWAgcGFja2FnZS4gV2VsbCB0aGF0IHBhY2thZ2UgaXMgaGVyZSB0byBzaW1wbGlmeSBvdXIgbGl2ZXMgYWdhaW4hIFJhdGhlciB0aGFuIGdlbmVyYXRpbmcgdGhlIHBhaXJlZCBkYXRhIGJldHdlZW4gZWFjaCBleHBlcmltZW50IG91cnNlbHZlcywgd2UgY2FuIHBhc3MgYWxvbmcgdGhlIHJlYWQgY291bnQgbWF0cml4IHdlIGp1c3QgZ2VuZXJhdGVkIHRvIHRoZSBgZ2dwYWlycygpYCBmdW5jdGlvbi4gUGFyYW1ldGVycyBvZiB0aGUgYGdncGFpcnMoKWAgZnVuY3Rpb24gdGhhdCB3ZSdsbCB1c2UgaW5jbHVkZToNCg0KLSAgIGBkYXRhYDogdGhlIGRhdGFzZXQgY29udGFpbmluZyBvdXIgZGF0YS4gSXQgY2FuIGluY2x1ZGUgYm90aCBudW1lcmljYWwgYW5kIGNhdGVnb3JpY2FsIGRhdGEuDQotICAgYG1hcHBpbmdgOiB0aGUgYWVzdGhldGljIG1hcHBpbmcgKGFzaWRlIGZyb20gYHhgIGFuZCBgeWApIHVzZWQgZm9yIGFsdGVyaW5nIHlvdXIgZm9ybWF0Lg0KLSAgIGBjb2x1bW5zYDogd2hpY2ggY29sdW1ucyBmcm9tIGBkYXRhYCBhcmUgdXNlZCB0byBnZW5lcmF0ZSB0aGUgcGxvdC4gRGVmYXVsdHMgdG8gYWxsIGNvbHVtbnMuDQotICAgYHRpdGxlYCwgYHhsYWJgLCBgeWxhYmA6IHRoZSB0aXRsZXMgb2YgeW91ciB2YXJpb3VzIGdyYXBoIGNvbXBvbmVudHMuDQotICAgYHVwcGVyYCBhbmQgYGxvd2VyYDogbmFtZWQgbGlzdHMgdGhhdCBtYXkgY29udGFpbiB0aGUgdmFyaWFibGVzICJjb250aW51b3VzIiwgImNvbWJvIiwgImRpc2NyZXRlIiwgYW5kICJuYSIgdXNlZCB0byBkZXRlcm1pbmUgaG93IHBhaXJ3aXNlIGNvbWJpbmF0aW9ucyBvZiBjb250aW51b3VzIGFuZC9vciBjYXRlZ29yaWNhbCBkYXRhIGFyZSBwbG90dGVkIGFjcm9zcyB0aGUgZ3JpZC4gWW91IGJhc2ljYWxseSBuZWVkIHRvIGNob29zZSBob3cgdGhlc2UgZGF0YSBjb21iaW5hdGlvbnMgd2lsbCBiZSBwbG90dGVkLg0KLSAgIGBkaWFnYDogYSBuYW1lZCBsaXN0IHRoYXQgbWF5IG9ubHkgY29udGFpbiB0aGUgdmFyaWFibGVzICJjb250aW51b3VzIiwgImRpc2NyZXRlIiwgYW5kICJuYSIuIEVhY2ggb2Ygd2hpY2ggaXMgc2V0IHRvIGEgc3BlY2lmaWMga2luZCBvZiBwbG90IHR5cGUgKGllICJkZW5zaXR5RGlhZyIsICJiYXJEaWFnIiwgb3IgImJsYW5rRGlhZyIpLg0KDQpNb3Jlb3ZlciwgeW91IG1heSByZWNhbGwgdGhhdCBgR0dhbGx5YCB3b3JrcyB3aXRoIHRoZSBgZ2dwbG90YCBvciBpcyBgZ2dwbG90YC0qZXh0ZW5zaWJsZSogc28gd2UgY2FuIHVzZSBtYW55IG9mIHRoZSBzYW1lIGZlYXR1cmVzIGZyb20gZ2dwbG90IHRvIGZpeCBzb21lIG9mIHRoZSBhZXN0aGV0aWNzIG9mIHRoZSByZXN1bHQgb2JqZWN0LiBJbiB0aGUgZW5kLCB5b3UnbGwgbm90aWNlIHRoYXQgdGhpcyBpcyBiYXNpY2FsbHkgYSBmYWNldGVkIHNjYXR0ZXJwbG90IGJ1dCB0aGUgZGF0YWZyYW1lIHJlcXVpcmVkIHRvIGdlbmVyYXRlIGl0IGlzIG1vcmUgY29tcGxleCB0aGFuIHdoYXQgd2UgY3VycmVudGx5IGhhdmUuIEZvciBtb3JlIGluZm9ybWF0aW9uIG9uIHRoaXMgZnVuY3Rpb24sIHlvdSBjYW4gY2hlY2sgb3V0IHRoZSBbYEdHYWxseSBtYW51YWxgXShodHRwczovL2NyYW4uci1wcm9qZWN0Lm9yZy93ZWIvcGFja2FnZXMvR0dhbGx5L0dHYWxseS5wZGYpIG9yIGNoZWNrIG91dCB0aGUgW2xpc3Qgb2YgZ3JlYXQgZXhhbXBsZXMgcHJvdmlkZWQgb24gdGhlIGF1dGhvcnMnIGdpdGh1Yl0oaHR0cHM6Ly9nZ29iaS5naXRodWIuaW8vZ2dhbGx5L3JlZmVyZW5jZS9nZ3BhaXJzLmh0bWwpLg0KDQpgYGB7ciwgZmlnLndpZHRoPTE0LCBmaWcuaGVpZ2h0PTE0fQ0KDQojIENyZWF0ZSB5b3VyIG1hdHJpeCBzY2F0dGVycGxvdCB3aXRoIGdncGFpcnMNCmdncGFpcnMocmVhZGNvdW50c18yNGh2NzJoLCANCiAgICAgICAgDQogICAgICAgICMgU2V0IHRoZSBhbHBoYSBvZiBvdXIgcG9pbnRzIHNvIHdlIGNhbiBzZWUgdGhlbSBiZXR0ZXINCiAgICAgICAgbWFwcGluZyA9IGFlcyhhbHBoYSA9IDAuMSksIA0KICAgICAgICANCiAgICAgICAgIyBXZSdsbCBwbGF5IHdpdGggdGhlIGNvcnJlbGF0aW9uIHRleHQgc28gdGhlIHZhbHVlcyBhcmUgbGl0dGxlIGxhcmdlciB0aGFuIGRlZmF1bHQgZm9yIHVzDQogICAgICAgIHVwcGVyID0gbGlzdChjb250aW51b3VzID0gd3JhcCgiY29yIiwgc2l6ZSA9IDkpKQ0KICAgICAgICkgKyAjIFNldCB0aGUgYWxwaGEgb2Ygb3VyIHBvaW50cyBzbyB3ZSBjYW4gc2VlIHRoZW0gYmV0dGVyDQogICAgIyBDaGFuZ2Ugc29tZSBvZiB0aGUgdGhlbWUgYXNwZWN0cyBsaWtlIHRleHQgc2l6ZQ0KICAgIHRoZW1lKHRleHQgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDIwKSwNCiAgICAgICAgICAjIFJvdGF0ZSB0aGUgeC1heGlzIHRpY2sgdGV4dCB0byBiZSANCiAgICAgICAgICBheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDkwLCB2anVzdCA9IDAuNSwgaGp1c3QgPSAxKSkNCmBgYA0KDQotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCg0KOjo6IHsuYWxlcnQgLmFsZXJ0LWJsb2NrIC5hbGVydC1zdWNjZXNzfQ0KKipXb3JrIHNtYXJ0ZXIsIG5vdCBoYXJkZXI6KiogV2hlbiBpdCBjb21lcyB0byBjZXJ0YWluIGNvbXBsZXggdmlzdWFsaXphdGlvbnMsIGl0IGlzIGJlc3QgdG8gYXZvaWQgcmUtaW52ZW50aW5nIHRoZSB3aGVlbC4gVGhlcmUgYXJlIHNvIG1hbnkgcGFja2FnZXMgYXZhaWxhYmxlIHRoYXQgYWNjb21wbGlzaCBhIGdyZWF0IG51bWJlciBvZiBkaWZmZXJlbnQgdmlzdWFsaXphdGlvbnMuIFRoZSBrZXkgaXMgdG8ga25vd2luZyB3aGF0ICpraW5kKiBvZiB2aXN1YWxpemF0aW9uIHlvdSB3YW50IHRvIHByb2R1Y2UsIGFuZCB0aGVuIGNoZWNraW5nIGlmIGEgcGFja2FnZSBleGlzdHMuIFdoaWxlIHRoZSAqKmdncGxvdCoqIHBhY2thZ2UgY2FuIGFjY29tcGxpc2ggcXVpdGUgYSBmZXcgYmFzaWMgYW5kIGNvbXBsZXhseSBsYXllcmVkIHZpc3VhbGl6YXRpb25zLCBvbmVzIGxpa2UgdGhlIGFib3ZlIHNjYXR0ZXJwbG90IG1hdHJpeCByZXF1aXJlIHRoZSBnZW5lcmF0aW9uIGFuZCByZXBsYWNlbWVudCBvZiBtdWx0aXBsZSBwbG90IHR5cGVzLiBUaGlzIHJlcXVpcmVzIGdvaW5nIHRvIGEgZGVlcGVyIGxldmVsIG9mIGtub3dsZWRnZSBiZXlvbmQgdGhpcyBjb3Vyc2UuIEEgbmljZSBwYWNrYWdlIGxpa2UgKipHR2FsbHkqKiB0YWtlcyBhd2F5IHRob3NlIGlzc3VlcyB3aGlsZSBvZmZlcmluZyB0aGUgYWJpbGl0eSB0byBzdGlsbCBjdXN0b21pemUgYW5kIGFsdGVyIHlvdXIgcGxvdCBmb3JtYXQgdG8gYSBjZXJ0YWluIGV4dGVudC4NCjo6Og0KDQojIyMgMS4zLjQgSW50ZXJwcmV0aW5nIGEgc2NhdHRlcnBsb3QgbWF0cml4DQoNCkFzIHlvdSBjYW4gc2VlLCB0aGUgc2NhdHRlcnBsb3QgbWF0cml4IGNhbiBwcm92aWRlIGEgcXVpY2sgd2F5IHRvIGNoZWNrIHRoYXQgeW91ciByZXBsaWNhdGVzIGFyZSBzaW1pbGFyLCB3aGlsZSB5b3VyIGNvbXBhcmlzb24gY29uZGl0aW9ucyBzaG91bGQgc2hvdyBtb3JlIHZhcmlhdGlvbiBiZXR3ZWVuIHRoZW0uIExvb2tpbmcgYXQgb3VyIGFib3ZlIHBsb3QsIHRoZSB0cmlhbmd1bGFyIHNlY3Rpb25zIG9mIHNjYXR0ZXJwbG90cyBpbiB0aGUgdG9wLWxlZnQgYW5kIGxvd2VyLXJpZ2h0IHJlcHJlc2VudCB0aGUgY29tcGFyaXNvbiBvZiByZXBsaWNhdGVzIGluIG91ciBleHBlcmltZW50LiBUaGVzZSBzaG91bGQgcHJvZHVjZSBzY2F0dGVyIHBvaW50cyB0aGF0IGFyZSBjbG9zZSB0byB0aGUgZGlhZ29uYWwgLSBtZWFuaW5nIHRoZSByZXBsaWNhdGVzIGFyZSBxdWl0ZSBzaW1pbGFyIGluIHRoZSBkYXRhIHByb2R1Y2VkLg0KDQpJbiB0aGUgbG93ZXItbGVmdCBjb3JuZXIgb2YgdGhlIHBsb3Qgd2UgZmluZCB0aGUgY29tcGFyaXNvbiBiZXR3ZWVuIG91ciB0d28gY29uZGl0aW9ucyAoMjQtIHZzIDcyLWhvdXIgZGF0YSkgYW5kIHlvdSBjYW4gc2VlIG11Y2ggbW9yZSBzY2F0dGVyIGF3YXkgZnJvbSB0aGUgZGlhZ29uYWwuIElmIG91ciBjb25kaXRpb25zIHJlYWxseSBkbyBpbmR1Y2UgZGlmZmVyZW50IHRyYW5zY3JpcHRpb25hbCBwcm9maWxlcywgdGhpcyBpcyB3aGF0IHdlIHNob3VsZCBleHBlY3RlZCB0byBzZWUuDQoNCkFjcm9zcyB0aGUgZGlhZ29uYWwgd2Ugc2VlIHRoZSBkZW5zaXR5IHBsb3Qgb2Ygb3VyIHJlYWQgY291bnRzLiBUaGVzZSBhcmVuJ3QgcGFydGljdWxhcmx5IGhlbHBmdWwgd2l0aCB0aGlzIGRhdGFzZXQgYXMgdGhlIGRhdGEgcHJlZG9taW5hbnRseSBhcHBlYXJzIHRvIGhhdmUgbG93IHJlYWQgY291bnRzIGV2ZW4gYWZ0ZXIgd2UgZmlsdGVyIGZvciBhIG1pbmltdW0gb2YgMTAgcmVhZHMgcGVyIGdlbmUuDQoNClRoaXMgdGVjaG5pcXVlIGlzIGFsc28gYSB1c2VmdWwgd2F5IHRvIGlkZW50aWZ5IHBvdGVudGlhbGx5IG1pc2xhYmVsZWQgc2FtcGxlcyBiZWZvcmUgcHJvY2Vzc2luZyB5b3VyIGRhdGEgZm9yIGFuYWx5c2lzLiBDaGFuZ2VzIGZyb20gdGhlIGFib3ZlIHBhdHRlcm4gY2FuIHNpZ25hbCB0aGUgbWlzbGFiZWxpbmcgb2Ygc2FtcGxlcyBvciBjaGFuZ2VzIHRvIHRoZSBwcm9jZXNzIG9mIHNhbXBsZSBwcmVwYXJhdGlvbi4NCg0KOjo6IHsuYWxlcnQgLmFsZXJ0LWJsb2NrIC5hbGVydC1zdWNjZXNzfQ0KKipOb3QganVzdCBmb3IgZXhwcmVzc2lvbiBhbmFseXNpcyEqKiBXaGlsZSB3ZSBoYXZlIHVzZSB0aGUgc2NhdHRlcnBsb3QgbWF0cmljZXMgaW4gdGhpcyBzZWN0aW9uIHRvIGNvbXBhcmUgb3VyIFJOQXNlcSBkYXRhc2V0cywgdGhlc2UgcGxvdCBmb3JtYXRzIGhhdmUgYSBtdWNoIGJyb2FkZXIgdXNlIGluIGRldGVybWluaW5nIGlmIGFueSBwb3NzaWJsZSBsaW5lYXIgY29ycmVsYXRpb24gbWF5IGV4aXN0IGJldHdlZW4gKmNvbnRpbnVvdXMgdmFyaWFibGVzKi4gSXQncyBhIHF1aWNrIHdheSB0byBsb29rIGF0IGEgd2lkZSBzY29wZSBvZiB2YXJpYWJsZXMgZm9yIHBvc3NpYmxlIHJlbGF0aW9uc2hpcHMgd2l0aG91dCBkb2luZyBhbGwgdGhlIGluLWRlcHRoIGFuYWx5c2lzIGZpcnN0LiBUaGluayBvZiBpdCBhcyB5ZXQgYW5vdGhlciBoZWxwZnVsIHRvb2wgZm9yIGV4cGxvcmF0b3J5IGRhdGEgYW5hbHlzaXMhDQo6OjoNCg0KLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQoNCiMjIDEuNC4wIFdoYXQgZG9lcyBkaWZmZXJlbnRpYWwgZXhwcmVzc2lvbiBkYXRhIGxvb2sgbGlrZT8NCg0KRGlnZ2luZyBkb3duIGludG8geW91ciBERSByZXN1bHRzLCB3aXRoIHRoZSBodW1hbiBnZW5vbWUsIHlvdSBhcmUgbG9va2luZyB0byBpZGVudGlmeSB0cmVuZHMgaW4gZ2VuZSBleHByZXNzaW9uIGRpZmZlcmVuY2VzIGFjcm9zcyA0MywwMDAgcG90ZW50aWFsIHRyYW5zY3JpcHRzIHNlcXVlbmNlcyAtIG9ubHkgaGFsZiBvZiB3aGljaCBwcm9kdWNlIHByb3RlaW5zLiBZb3UgY2FuIGltYWdpbmUgdGhhdCBvcGVuaW5nIHVwIGEgdGFidWxhciBmaWxlIG9mIHRoYXQgc2l6ZSBjb3VsZCB0YWtlIGEgYml0IG9mIHRpbWUuDQoNCkZvciBlYWNoIERFIGV4cGVyaW1lbnQgdGhlcmUgYXJlIGEgbnVtYmVyIG9mIHZhbHVlcyBnZW5lcmF0ZWQuIEluIGEgcG9wdWxhciBwYWNrYWdlIGxpa2UgYERFU2VxMmAgeW91IHdpbGwgdHlwaWNhbGx5IGZpbmQ6DQoNCnwgVmFyaWFibGUgICAgICAgICAgICAgIHwgRGVzY3JpcHRpb24gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8DQp8Oi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tfDotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLXwNCnwgR2VuZSBuYW1lICAgICAgICAgICAgIHwgVGhlIG5hbWVzIG9mIHlvdXIgZ2VuZXMgd2hpY2ggbWF5IGJlIGluIGRpZmZlcmVudCBmb3JtYXRzIGxpa2UgRW50cmV6IElELCBFbnNlbWJsLCBvciBnZW5lIHN5bWJvbCAgICAgICAgICAgICAgICB8DQp8IEJhc2UgbWVhbiAgICAgICAgICAgICB8IEF2ZXJhZ2Ugb2YgdGhlIG5vcm1hbGl6ZWQgY291bnRzIHZhbHVlcywgYWNjb3VudGluZyBmb3Igc2l6ZSBmYWN0b3JzLCB0YWtlbiBvdmVyIGFsbCBzYW1wbGVzIChhbmQgb3IgcmVwbGljYXRlcykgfA0KfCBMb2ckX3syfSQgZm9sZC1jaGFuZ2UgfCBUaGUgZWZmZWN0IHNpemUgZXN0aW1hdGUgLSBob3cgbXVjaCB5b3VyIGdlbmUncyBleHByZXNzaW9uIGhhcyBjaGFuZ2VkIGZyb20gY29udHJvbC9iYXNlIGNvbmRpdGlvbnMgICAgICAgICAgICAgIHwNCnwgTG9nIGZvbGQtY2hhbmdlIFNFICAgIHwgQW4gZXN0aW1hdGUgb2YgZWZmZWN0IHNpemUgdW5jZXJ0YWludHkgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8DQp8IHAtdmFsdWUgICAgICAgICAgICAgICB8IEh5cG90aGVzaXMgdGVzdCBhZ2FpbnN0IEgkX3swfSQgdGhhdCBubyBkaWZmZXJlbmNlIGV4aXN0cyBiZXR3ZWVuIHNhbXBsZSBncm91cHMgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfA0KfCBhZGp1c3RlZCBwLXZhbHVlICAgICAgfCBBbiBhZGp1c3RlZCBwLXZhbHVlIGFmdGVyIHRha2luZyBpbnRvIGFjY291bnQgbXVsdGlwbGUgdGVzdGluZyBiZXR3ZWVuIGdyb3Vwcy9zYW1wbGVzICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwNCg0KWW91IGNhbiBmaW5kIG1vcmUgaW5mb3JtYXRpb24gb24gd29ya2luZyB3aXRoIHRoZSBgREVTZXEyYCBwYWNrYWdlIFtoZXJlXShodHRwczovL2Jpb2MuaXNtLmFjLmpwL3BhY2thZ2VzLzIuMTQvYmlvYy92aWduZXR0ZXMvREVTZXEyL2luc3QvZG9jL2JlZ2lubmVyLnBkZikNCg0KLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQoNCiMjIDEuNS4wIFlvdSBjYW4gZXhhbWluZSBkYXRhIG9uIG1hbnkgc2NhbGVzDQoNCk9uY2Ugd2UgaGF2ZSBhIHNldCBvZiBkYXRhIHdlIGNhbiBleGFtaW5lIGl0IGZyb20gbWFueSBhc3BlY3RzOg0KDQoxLiAgQ29tcGFyZSBhbGwgZ2VuZXMgd2l0aGluIGEgc2FtcGxlIGFuZCBhY3Jvc3Mgc2FtcGxlcw0KMi4gIENvbXBhcmUgREUgaW4gYSBzdWJzZXQgb2YgZ2VuZXMgd2l0aGluIGEgc2FtcGxlIGFuZCBhY3Jvc3Mgc2FtcGxlcw0KMy4gIENvbXBhcmUgREUgcmVzdWx0cyBiYXNlZCBvbiBnZW5lIGZ1bmN0aW9uDQoNCkxldCdzIGV4cGxvcmUgdGhlc2UgZGlmZmVyZW50IGFzcGVjdHMgYW5kIHRoZSBraW5kIG9mIHBsb3RzIHdlIGNhbiBnZW5lcmF0ZSB0byBhY2NvbW9kYXRlIHRoZSBzaXplIG9mIG91ciBkYXRhIHNldC4gRmlyc3QsIGhvd2V2ZXIsIHdlIG5lZWQgdG8gZmluZCBhIHVzZWZ1bCBkYXRhIHNldCB0byB3b3JrIHdpdGguDQoNCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KDQojIyMgMS41LjEgVXNlIHRoZSBgcmVhZHhsYCBsaWJyYXJ5IHRvIG9wZW4geW91ciAueGxzeCBmaWxlcw0KDQpXZSd2ZSBicmllZmx5IHRvdWNoZWQgb24gdXNpbmcgdGhpcyBwYWNrYWdlIGluIGBMZWN0dXJlIDAxYCB0byBoZWxwIHJlYWQgb3VyIE1pY3Jvc29mdCBFeGNlbC1iYXNlZCBkYXRhLiBUb2RheSdzIGRhdGEgc2V0IGZyb20gQmxhbmMtTWVsbyBldCBhbC4gKENlbGwgMjAyMCksIGNvbWVzIHRvIHVzIGluIHRoZSBmb3JtIG9mIGFuIGV4Y2VsIGZpbGUuIExldCdzIHVzZSBzb21lIG9mIHRoZSB0b29scyBmcm9tIHRoaXMgcGFja2FnZSB0byBoZWxwIG9wZW4gdXAgb3VyIGRhdGEgZmlsZS4gV2UnbGwgc3RhcnQgYnkgcGVla2luZyBhdCBob3cgbWFueSBzaGVldHMgdGhlcmUgYXJlIHVzaW5nIGBleGNlbF9zaGVldHMoKWAuDQoNCmBgYHtyfQ0KIyBXaGF0IGFyZSB0aGUgc2hlZXRzIHRvIG9wZW4gaW4gb3VyIGRhdGEgc2V0Pw0KZXhjZWxfc2hlZXRzKCJkYXRhL0JsYW5jby1NZWxvMjAyMENlbGwuU3VwcDEueGxzeCIpDQpgYGANCg0KLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQoNCiMjIyAxLjUuMiBPcGVuIGVhY2ggc2hlZXQgc2VwYXJhdGVseSB3aXRoIGByZWFkX2V4Y2VsKClgDQoNCk5vdyB0aGF0IHdlIGNhbiBzZWUgdGhlcmUgYXJlIHR3byBzaGVldHMgaW4gb3VyIGRhdGEsIHdlIGNhbiBhc3NpZ24gZWFjaCBvbmUgdG8gYSBkaWZmZXJlbnQgZGF0YSBmcmFtZSB1c2luZyB0aGUgYHJlYWRfZXhjZWwoKWAgY29tbWFuZCB3aGljaCBjb250YWlucyB0aGUgcGFyYW1ldGVyIGBzaGVldGAuIFdlIGNhbiB1c2UgdGhpcyB0byBkZXRlcm1pbmUgd2hpY2ggc2hlZXQgdG8gbG9hZCBlaXRoZXIgYmFzZWQgb24gaXQncyBwb3NpdGlvbiAoaW50ZWdlcikgb3IgaXQncyBuYW1lIGFzIHNwZWNpZmllZCBieSBgZXhjZWxfc2hlZXRzKClgLg0KDQpgYGB7cn0NCiMgQXNzaWduIG91ciBsZWdlbmQgDQpibGFuY29fbGVnZW5kLmRmIDwtIHJlYWRfZXhjZWwoIi4vZGF0YS9CbGFuY28tTWVsbzIwMjBDZWxsLlN1cHAxLnhsc3giLCBzaGVldD0xKQ0KDQojIEFzc2lnbiBvdXIgZGF0YQ0KYmxhbmNvX2RhdGEuZGYgPC0gcmVhZF9leGNlbCgiLi9kYXRhL0JsYW5jby1NZWxvMjAyMENlbGwuU3VwcDEueGxzeCIsIHNoZWV0PTIpDQoNCnN0cihibGFuY29fbGVnZW5kLmRmKQ0Kc3RyKGJsYW5jb19kYXRhLmRmKQ0KYGBgDQoNCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KDQojIyMgMS41LjMgQUJXOiBBbHdheXMgQmUgV3JhbmdsaW5nICh5b3VyIGRhdGEpDQoNClllcywgaXQgbG9va3MgbGlrZSB0aGUgbGVnZW5kIGhhcyBzb21lIGhlbHBmdWwgaW5mb3JtYXRpb24gYWJvdXQgdGhlIGRhdGFzZXQgaXRzZWxmIGZvdW5kIGluIHRoZSBzZWNvbmQgc2hlZXQuIFRoZSBERSBkYXRhIGxvb2tzIGxpa2UgaXQncyBzcGxpdCBhY3Jvc3MgMjAgY29sdW1ucyBlbmNvbXBhc3NpbmcgMTAgZXhwZXJpbWVudGFsIGRhdGEgc2V0cy4gRm9yIGVhY2ggZGF0YSBzZXQsIGl0IGxvb2tzIGxpa2UgdGhlcmUgaXMgYSBMb2ckX3syfSQgZm9sZC1jaGFuZ2UgdmFsdWUgKCoqTDJGQyoqKSBhbmQgYW4gYWRqdXN0ZWQgcC12YWx1ZSAoKipwYWRqKiopLg0KDQpMZXQncyB0YWtlIGEgY2xvc2VyIGxvb2sgYXQgYGJsYW5jb19sZWdlbmQuZGZgIGZpcnN0LiBGcm9tIGl0IHdlIGNhbiBwYXJzZSBvdXQgc29tZSBpbXBvcnRhbnQgaW5mb3JtYXRpb24gYWJvdXQgdGhlIGV4cGVyaW1lbnRzIHRoZW1zZWx2ZXMgbGlrZSB0aGUgKipwYXRob2dlbioqIGFuZCAqKmhvc3QqKiBpbiBlYWNoIGV4cGVyaW1lbnRhbCBzZXQuDQoNCmBgYHtyfQ0KIyBMb29rIGF0IHRoZSBlbnRpcmUgbGVnZW5kDQpibGFuY29fbGVnZW5kLmRmDQpgYGANCg0KLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQoNCiMjIyAxLjUuNCBFeHRyYWN0IHN1YnN0cmluZyBwYXR0ZXJucyBhbmQgc2F2ZSB0aGVtIHVzaW5nIGBzdHJfbWF0Y2hfYWxsKClgDQoNCllvdSBtYXkgcmVtZW1iZXIgdGhhdCB3ZSBjYW4gZWFzaWx5IG1hdGNoIGZvciBwYXR0ZXJucyBpbiBvdXIgc3RyaW5nIHVzaW5nIGZ1bmN0aW9ucyBmcm9tIHRoZSBgc3RyaW5ncmAgcGFja2FnZSBidXQgd2l0aCBhIGZ1bmN0aW9uIGxpa2UgYHN0cl9tYXRjaF9hbGwoKWAgeW91IGNhbiBhbHNvIGluY2x1ZGUgbWF0Y2hpbmcgKmdyb3VwcyogdG8geW91ciBwYXR0ZXJuIHRoYXQgY2FuIGJlIGNhcHR1cmVkLiBGb3IgKmVhY2gqIHN0cmluZyBtYXRjaCwgdGhpcyBmdW5jdGlvbiB3aWxsIHJldHVybiBhIG1hdHJpeCB0aGF0IGNvbnRhaW5zIHRoZSBjb21wbGV0ZSBtYXRjaGluZyBwYXR0ZXJuIGFuZCBhbnkgZ3JvdXBlZCBwYXR0ZXJucyB0aGF0IGFyZSBkZW5vdGVkIGJ5IHRoZSBgKClgIHBhcmVudGhlc2VzLg0KDQoqSWYqIGEgc3RyaW5nIGNvbnRhaW5zIG11bHRpcGxlIG1hdGNoZXMgdG8geW91ciBwYXR0ZXJuLCBpdCB3aWxsIGdlbmVyYXRlIGFkZGl0aW9uYWwgcm93cyBpbiB0aGUgbWF0cml4IGZvciB0aGF0IHN0cmluZy4NCg0KTG9va2luZyBhdCBvdXIgYE5vdGVzYCBjb2x1bW4gb2YgYGJsYW5jb19sZWdlbmQuZGZgLCBpdCBsb29rcyBsaWtlIGl0IGZvbGxvd3MgYSByZWd1bGFyIHBhdHRlcm4gd2VyZSB3ZSBjYW4gZXh0cmFjdCB0aGUgcGF0aG9nZW4gYW5kIGhvc3QgY2VsbCBpbmZvcm1hdGlvbg0KDQp8IHNlY3Rpb24gICAgICAgICAgICAgIHwgRXhhbXBsZXMgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8DQp8Oi0tLS0tLS0tLS0tLS0tLS0tLS0tLXw6LS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS18DQp8IFBhdGhvZ2VuIG5hbWUgICAgICAgIHwgU0FSUy1Db1YtMiwgUlNWICAgICAgICAgICAgICAgICAgICAgICB8DQp8IEludGVybWVkaWF0ZSBzdHJpbmdzIHwgaW5mZWN0aW9uLCBpbmZlY3Rpb24gd2l0aCBSdXhvbGl0aW5pYiB8DQp8IEhvc3QgY2VsbCB0eXBlICAgICAgIHwgQTU0OSwgQTU0OS1BQ0UyICAgICAgICAgICAgICAgICAgICAgICB8DQoNCmBgYHtyfQ0KIyBwYXR0ZXJuIG1hdGNoIGZvciBwYXRob2dlbiBhbmQgaG9zdCBpbmZvcm1hdGlvbiBhbmQgc2F2ZSBpdCBpbnRvIGEgZGF0YWZyYW0geW91IGNhbiBhY2Nlc3MgbGF0ZXIuDQoNCmV4cF9pbmZvIDwtDQogIGJsYW5jb19sZWdlbmQuZGYgJT4lIA0KICANCiAgIyBVc2UgZHBseXI6OnNlbGVjdCBiZWNhdXNlIHRoZSBzZWxlY3QgZnVuY3Rpb24gaGFzIGJlZW4gb3ZlcnJpZGRlbiBieSBhbm90aGVyIGxpYnJhcnkNCiAgZHBseXI6OnNlbGVjdChOb3RlcykgJT4lIA0KICANCiAgIyBVc2Ugc3RyX21hdGNoX2FsbCB0byBncmFiIHRoZSBwYXR0ZXJuIHdlIHdhbnQsIHBpZWNlIGJ5IHBpZWNlDQogICMgZXhhbXBsZSBzdHJpbmc6IFNBUlMtQ29WLTIgaW5mZWN0aW9uIChBNTQ5LUFDRTIgY2VsbHMsIE1PSTogMiwgMjRocGkpDQogIHN0cl9tYXRjaF9hbGwocGF0dGVybiA9IHIiKChbXHctXSspW1xzXHddK1woKFtcdy1dKylccypjZWxscykiKSAlPiUgDQogIA0KICAjIFNldCBpbnRvIGEgZGF0YSBmcmFtZQ0KICBhcy5kYXRhLmZyYW1lKCkgJT4lIA0KICANCiAgIyBDaGFuZ2UgdGhlIGNvbHVtbiBuYW1lcyAod291bGQgYmUgWDEsIFgyLCBYMyBvdGhlcndpc2UpDQogIGBjb2xuYW1lczwtYChjKCJub3RlcyIsICJwYXRob2dlbiIsICJob3N0IikpDQogIA0KICAjIEFsdGVybmF0aXZlIGNvZGUgdG8gc2V0IG5hbWVzOiANCiAgIyBtYWdyaXR0cjo6c2V0X2NvbG5hbWVzKGMoIm5vdGVzIiwgInBhdGhvZ2VuIiwgImhvc3QiKSkNCg0KIyBUYWtlIGEgbG9vayBhdCB0aGUgcmVzdWx0aW5nIGRhdGENCmV4cF9pbmZvDQpgYGANCg0KYGBge3J9DQojIEFkZCB0aGlzIG5ldyBpbmZvcm1hdGlvbiB0byBvdXIgb3JpZ2luYWwgbGVnZW5kIGluZm9ybWF0aW9uDQoNCmJsYW5jb19leHBfaW5mby5kZiA8LSANCiAgYmxhbmNvX2xlZ2VuZC5kZiAlPiUgDQogIA0KICAjIE1ha2Ugc29tZSBuZXcgY29sdW1ucyB3aXRoIHBhdGhvZ2VuIGFuZCBob3N0IGluZm9ybWF0aW9uDQogIG11dGF0ZShwYXRob2dlbiA9IGV4cF9pbmZvJHBhdGhvZ2VuLCAjIE91ciBwYXRob2dlbiBpbmZvcm1hdGlvbg0KICAgICAgICAgaG9zdCA9IGV4cF9pbmZvJGhvc3QpICU+JSAjIE91ciBob3N0IGluZm9ybWF0aW9uIA0KICANCiAgIyBDb21iaW5lIG91ciBwYXRob2dlbiBhbmQgaG9zdCBpbmZvcm1hdGlvbiBpbnRvIGEgc2luZ2xlIGNvbHVtbiBhcyB3ZWxsDQogIHVuaXRlKGNvbCA9IC4uLiwgLi4uLCBzZXA9Il8iLCByZW1vdmUgPSBGQUxTRSkgJT4lIA0KICANCiAgIyBKdXN0IGtlZXAgdGhlIG9yaWdpbmFsIEZpZWxkIGNvbHVtbiBhbmQgdGhlIG5ldyBvbmVzDQogIGRwbHlyOjpzZWxlY3QoMSwgNCwgNSwgNikgDQoNCiMgVGFrZSBhIGxvb2sgYXQgd2hhdCB3ZSBhcmUgd29ya2luZyB3aXRoDQpibGFuY29fZXhwX2luZm8uZGYNCmBgYA0KDQotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCg0KIyMjIDEuNS41IENvbnZlcnQgb3VyIERFIGRhdGEgdGFibGUgaW50byBsb25nLWZvcm1hdCBhbmQgam9pbiB3aXRoIG91ciBjZWxsIGluZm9ybWF0aW9uDQoNCk5vdyB0aGF0IHdlIGhhdmUgY2F0YWxvZ3VlZCBvdXIgZXhwZXJpbWVudGFsIGluZm9ybWF0aW9uLCBsZXQncyBidWlsZCB0aGF0IGludG8gYSBsb25nLWZvcm1hdCB2ZXJzaW9uIG9mIG91ciBhY3R1YWwgREUgZGF0YXNldCBmb3VuZCBpbiBgYmxhbmNvX2RhdGEuZGZgLiBUaGUgc3RlcHMgd2UnbGwgdGFrZSB0byBwcmVwYXJlIG91ciBkYXRhIGFyZQ0KDQoxLiAgQ29udmVydCBmcm9tIHdpZGUgdG8gbG9uZyBmb3JtYXQgd2l0aCBgcGl2b3RfbG9uZ2VyKClgLg0KDQoyLiAgSm9pbiB3aXRoIG91ciBleHBlcmltZW50YWwgZGF0YSBpbmZvcm1hdGlvbiBpbiBgYmxhbmNvX2V4cF9pbmZvLmRmYA0KDQozLiAgUmVwbGFjZSBvdXIgZXhwZXJpbWVudCBpbmZvcm1hdGlvbiAoZm9ybWVybHkgY29sdW1uIGluZm9ybWF0aW9uKSB0byBhIGNvbnNpc3RlbnQgZm9ybWF0IG9mIGBleHBlcmltZW50X3ZhbHVlYCBzbyB3ZSBjYW4gc3BsaXQgaXQgb3V0IHByb3Blcmx5Lg0KDQo0LiAgU2VwYXJhdGUgb3VyIGBleHBlcmltZW50X3ZhbHVlYCBpbmZvcm1hdGlvbi4gQ3VycmVudGx5IHdlJ2xsIGhhdmUgZXhwZXJpbWVudHMgbWFwcGluZyB0byBib3RoIGFuIEwyRkMgYW5kIHBhZGogdmFsdWUNCg0KNS4gIENvbnZlcnQgb3VyIGRhdGEgYmFjayBvdXQgYSBsaXR0bGUgYml0IHRvIGEgc2xpZ2h0bHkgd2lkZXIgZm9ybWF0IHdpdGggYHBpdm90X3dpZGVyKClgDQoNCmBgYHtyfQ0KYmxhbmNvX2RhdGFfbG9uZy5kZiA8LQ0KICBibGFuY29fZGF0YS5kZiAlPiUgDQogIA0KICAjIGNvbGxhcHNlIGFsbCBvZiB0aGUgZGF0YSBjb2x1bW5zDQogIHBpdm90X2xvbmdlcihjb2xzPSgyOjIxKSwgbmFtZXNfdG8gPSAiZXhwZXJpbWVudCIsIHZhbHVlc190byA9ICJ2YWx1ZSIpICU+JSANCiAgDQogICMgYWRkIG91ciBleHBlcmltZW50YWwgaW5mbyB0byBlYWNoIG9ic2VydmF0aW9uLiBOb3RlIHRoYXQgZWFjaCBnZW5lIGhhcyBtdWx0aXBsZSBvYnMgbm93Lg0KICBmdWxsX2pvaW4oLiwgYmxhbmNvX2V4cF9pbmZvLmRmLCBieT1jKCJleHBlcmltZW50IiA9IC4uLikpICU+JSANCiAgDQogICMgV2UgbmVlZCB0byBmaXggYWxsIG9mIHRoZSAicGFkal9leHBlcmltZW50IiB2YWx1ZXMgdG8gImV4cGVyaW1lbnRfcGFkaiIgZm9ybWF0DQogICMgTm90ZSB3ZSBjb3VsZCBoYXZlIHJlbmFtZWQgb3VyIGNvbHVtbnMgQkVGT1JFIHBpdm90aW5nDQogIG11dGF0ZShleHBlcmltZW50ID0gc3RyX3JlcGxhY2VfYWxsKC4kZXhwZXJpbWVudCwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHBhdHRlcm49ciIoLi4uKSIsICMgQ2FwdHVyZSB0aGUgZ3JvdXBzDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICMgUHV0IHRoZW0gYmFjayB0b2dldGhlciBpbiBhIHN3aXRjaGVkIG9yZGVyDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHJlcGxhY2VtZW50ID0gciIoLi4uKSIpKSAlPiUgICAgDQogIA0KICAjIE5vdyB3ZSB3YW50IHRvIHNwbGl0IHRoZSBleHBlcmltZW50IGNvbHVtbiBpbnRvIHR3bzogZXhwZXJpbWVudCBhbmQgZGF0YV90eXBlIChMMkZDIG9yIHBhZGopDQogIHNlcGFyYXRlKGV4cGVyaW1lbnQsIGMoImV4cGVyaW1lbnQiLCAiZGF0YV90eXBlIiksIHNlcD0iXyIsIHJlbW92ZT1UUlVFKSAlPiUgDQogIA0KICAjIFBpdm90IG91dCB0aGUgZGF0YSBzbyB0aGF0IGVhY2ggb2JzZXJ2YXRpb24gZm9yIGVhY2ggZXhwZXJpbWVudCBoYXMgYW4gTDJGQyBhbmQgcGFkaiB2YWx1ZQ0KICBwaXZvdF93aWRlcihuYW1lc19mcm9tID0gZGF0YV90eXBlLCB2YWx1ZXNfZnJvbSA9IHZhbHVlKQ0KDQpibGFuY29fZGF0YV9sb25nLmRmDQpgYGANCg0KYGBge3J9DQojIERvbid0IGZvcmdldCB0byBzYXZlIHlvdXIgd3JhbmdsZWQgZGF0YSENCnNhdmUoYmxhbmNvX2RhdGFfbG9uZy5kZiwgZmlsZT0iLi9kYXRhL0xlY3R1cmUwNF9ibGFuY28uUkRhdGEiKQ0KYGBgDQoNCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KDQojIDIuMC4wIFZpc3VhbCBpbnNwZWN0aW9uIG9mIERFIGFjcm9zcyBhIHNhbXBsZQ0KDQpBZnRlciBhbGwgb2Ygb3VyIGRhdGEgd3JhbmdsaW5nLCB5b3UgY2FuIHNlZSB3ZSBoYXZlIGEgZGF0YSBmcmFtZSB3aXRoIG5lYXJseSAxLzQgbWlsbGlvbiBvYnNlcnZhdGlvbnMuIFRoaXMgc3BhbnMgYWNyb3NzIDEwIGV4cGVyaW1lbnRhbCBjb25kaXRpb25zLiBGcm9tIGEgYnJvYWQgbGV2ZWwsIHdlIGNhbiBsb29rIGFjcm9zcyBlbnRpcmUgZGF0YSBzZXRzIHRvIGlkZW50aWZ5IGdlbmVzIG9mIGludGVyZXN0LiBUaGVyZSBhcmUgYSBjb3VwbGUgb2Ygd2F5cyB0byBkbyB0aGlzIGFuZCB3ZSdsbCBiZWdpbiB3aXRoIGEgc3RhbmRhcmQgc2V0IG9mIHBsb3RzIHRoYXQgY2FuIGNvbnZleSB0aGUgb3ZlcmFsbCBkaXN0cmlidXRpb24gb2Ygb3VyIGRhdGEuIFdoaWxlIG91ciB2aWV3IG9mIHRoZSBkYXRhIGlzIGxvdy1yZXNvbHV0aW9uIHdlIGFyZSBzdGlsbCBhYmxlIHRvIGNvbXBhcmUgc3BlY2lmaWMgZ2VuZSBpbmZvcm1hdGlvbiBjZW50ZXJlZCBhcm91bmQgbWVhbiBleHByZXNzaW9uIGxldmVscywgbWFnbml0dWRlIG9mIGNvbXBhcmlzb24sIGFuZCBzdGF0aXN0aWNhbCBzaWduaWZpY2FuY2UuDQoNCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KDQojIyAyLjEuMCBUaGUgTUEgcGxvdCBkZXNjcmliZXMgdGhlIHNwcmVhZCBvZiBvdXIgREUgZGF0YQ0KDQpVc2VkIG9yaWdpbmFsbHkgaW4gdGhlIGFwcGxpY2F0aW9uIG9mIE1pY3JvYXJyYXkgZGF0YSBhbmFseXNpcywgdGhlc2UgcGxvdHMgYXJlIGFsc28gYXBwbGllZCB0byB2aXN1YWxpemluZyBoaWdoLXRocm91Z2hwdXQgc2VxdWVuY2luZyBhbmFseXNpcy4NCg0KVGhpcyBpcyBhIGRhdGEgdHJhbnNmb3JtYXRpb24gYmV0d2VlbiB0d28gc2V0cyBvZiBkYXRhIHN1Y2ggdGhhdA0KDQotICAgYE1gIChtaW51cyBpbiB0aGUgbG9nIHNjYWxlKSByZXByZXNlbnRzIHRoZSBsb2ckX3syfSQgcmF0aW8gb2YgZm9sZCBkaWZmZXJlbmNlcyBiZXR3ZWVuIHlvdXIgZGF0YSBzZXRzDQoNCi0gICBgQWAgKGF2ZXJhZ2UgaW4gdGhlIGxvZyBzY2FsZSkgcmVwcmVzZW50cyB0aGUgYXZlcmFnZSBleHByZXNzaW9uIHNpZ25hbA0KDQpXZSBwbG90IG91ciBgTWAgdmFsdWVzIGFsb25nIHRoZSB5LWF4aXMgYWdhaW5zdCB0aGUgY29ycmVzcG9uZGluZyBgQWAgdmFsdWVzIG9uIHRoZSB4LWF4aXMuIERvIHRoZXNlIGNoYXJhY3RlcmlzdGljcyBzb3VuZCBmYW1pbGlhcj8gVGhleSBhcmUgc3RhbmRhcmQgb3V0cHV0IGZvciBgREVTZXEyYCBkYXRhIHNldHMhIFVuZm9ydHVuYXRlbHkgb3VyIGFib3ZlIGRhdGEgZG9lc24ndCBoYXZlIHRoYXQgaW5mb3JtYXRpb24gYW55bW9yZSwgYnV0IHdlIGhhdmUgYSBkYXRhc2V0IHRoYXQgZG9lcyEgSXQncyBhbm90aGVyIGRhdGFzZXQgZnJvbSBhbiBlYXJsaWVyIHByZS1wdWJsaWNhdGlvbiBjb3B5IG9mIHRoZSBCbGFuY28tTWVsbyBldCBhbC4sIDIwMjAgcGFwZXI6IGBCbGFuY28tTWVsbzIwMjBfU3VwcF9EYXRhXzQueGxzeGAuDQoNCkxldCdzIG9wZW4gdGhpcyB1cCBhbmQgdGFrZSBhIGxvb2shDQoNCmBgYHtyfQ0KIyBXZSdsbCBiZSBsb29raW5nIGF0IERFIGRhdGEgZm9yIElBViBpbmZlY3Rpb24gb2YgQTU0OSBjZWxscw0KREVfQTU0OV9JQVYuZGYgPC0gcmVhZF9leGNlbCguLi4sIHNoZWV0PTIpDQoNCiMgTG9vayBhdCB0aGUgZGF0YSBmcmFtZSB3ZSd2ZSBtYWRlDQpoZWFkKERFX0E1NDlfSUFWLmRmKQ0KYGBgDQoNCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KDQojIyMgMi4xLjEgQUJtVzogQSBCaXQgbW9yZSBXcmFuZ2xpbmcgd2l0aCBgYWNyb3NzKClgDQoNCk9rYXksIHNvIHdlIGNhbid0IHVzZSB0aGUgZGF0YSAqcmlnaHQqIGF3YXkuIFRoZSBgcmVhZF9leGNlbCgpYCBjb21tYW5kIGhhcyBkZWNpZGVkIHRvIGNvbnZlcnQgc29tZSBvZiBvdXIgY29sdW1ucyB0byBjaGFyYWN0ZXIgZm9ybWF0IGJlY2F1c2UgaXQgZGV0ZWN0ZWQgYE5hTmAgdmFsdWVzLiBTbyB3ZSdsbCBoYXZlIHRvIHF1aWNrbHkgY29lcmNlIHRoZSBkYXRhIGJlZm9yZSBtb3Zpbmcgb24uIFdlIGNhbiBxdWlja2x5IGNvbnZlcnQgdGhlIGRhdGEgd2l0aCBhIGBtdXRhdGUoKWAgY29tbWFuZCB1c2luZyB0aGUgaGVscGVyIHZlcmIgYGFjcm9zcygpYC4NCg0KVG8gc3VjY2Vzc2Z1bGx5IHVzZSB0aGlzIGhlbHBlciwgd2Ugd2lsbCB3YW50IHRvIGxpc3Qgb3V0IHRoZSBjb2x1bW5zIHdlIHdhbnQgdG8gdXNlIGluIHRoZSBgLmNvbHNgIHBhcmFtZXRlciwgYW5kIHRoZW4gYXBwbHkgYSBmdW5jdGlvbiBsaWtlIGBhcy5udW1lcmljYC4gV2UgZG9uJ3QgYWN0dWFsbHkgbmVlZCAqYWxsKiBvZiB0aGVzZSBjb2x1bW5zLCBidXQgaXQncyBnb29kIHByYWN0aWNlIQ0KDQpgYGB7cn0NCiMgQ29udmVydCBvdXIgbnVtZXJpYyBjb2x1bW5zIHRvIHRoZSBwcm9wZXIgdHlwZS4NCkRFX0E1NDlfSUFWLmRmIDwtIA0KICBERV9BNTQ5X0lBVi5kZiAlPiUNCiAgDQogICMgVXNlIG11dGF0ZSgpIHdpdGggYWNyb3NzKCkgdG8gY29udmVydCBjb2x1bW5zIHRvIG51bWVyaWMNCiAgbXV0YXRlKC4uLikNCg0KIyBDaGVjayB0aGUgcmVzdWx0DQpzdHIoREVfQTU0OV9JQVYuZGYpDQpgYGANCg0KLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQoNCjo6OiB7LmFsZXJ0IC5hbGVydC1ibG9jayAuYWxlcnQtd2FybmluZ30NCioqU2tpcCB0aGUgd3JhbmdsaW5nIHdpdGggYSBwcm9wZXIgaW1wb3J0ISoqIFdoaWxlIHdlIHVzZWQgdGhlIGFib3ZlIGltcG9ydCBhcyBhbiBvcHBvcnR1bml0eSB0byBwcmFjdGljZSBvdXIgd3JhbmdsaW5nIHNraWxscywgd2UgY291bGQgaGF2ZSBhdm9pZGVkIGl0IGFsdG9nZXRoZXIgaWYgd2UgaW1wb3J0ZWQgcHJvcGVybHkuIFdpdGhpbiB0aGUgKipyZWFkX2V4Y2VsKCkqKiBmdW5jdGlvbiB0aGVyZSBpcyBhIHBhcmFtZXRlciBjYWxsZWQgKioibmEiKiogd2hpY2ggYWxsb3dzIHVzIHRvIGRlZmluZSB0aGUgY2hhcmFjdGVyKHMpIHRoYXQgcmVwcmVzZW50IG51bGwgb3IgTkEgdmFsdWVzLiBUaGUgZGVmYXVsdCBpcyBzaW1wbHkgYmxhbmsgb3IgZW1wdHkgY2VsbHMgYnV0IGtub3dpbmcgd2hhdCB3ZSBkbyBub3cgYWJvdXQgdGhlIGRhdGFmcmFtZSwgd2UgY291bGQgYWxzbyBoYXZlIHVzZWQgc2V0IHRoZSBwYXJhbWV0ZXI6ICoqbmEgPSAiTmFOIioqIHdoaWNoIHdvdWxkIGFsbG93IG91ciBkYXRhZnJhbWUgdG8gcHJvcGVybHkgaW1wb3J0IHRoZSB2YWx1ZXMgYW5kIGNvcnJlY3RseSBhc3NpZ24gdmFyaWFibGUgdHlwZXMuIFNvLCBpdCdzIGFsd2F5cyBoZWxwZnVsIHRvIFtrbm93IHdoYXQgeW91ciBmdW5jdGlvbnMgY2FuIGRvIV0oJTVCaHR0cHM6Ly9yZWFkeGwudGlkeXZlcnNlLm9yZy9yZWZlcmVuY2UvcmVhZF9leGNlbC5odG1sJTVEKGh0dHBzOi8vcmVhZHhsLnRpZHl2ZXJzZS5vcmcvcmVmZXJlbmNlL3JlYWRfZXhjZWwuaHRtbCkpDQo6OjoNCg0KLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQoNCiMjIyAyLjEuMiBCdWlsZCB5b3VyIE1BIHBsb3QgdXNpbmcgYGdncGxvdDJgDQoNCkdpdmVuIHRoYXQgd2UgYWxyZWFkeSBoYXZlIG91ciBkYXRhIGluIGEgZGlmZmVyZW50aWFsIGV4cHJlc3Npb24gZm9ybWF0LCB3ZSBjYW4ganVzdCB1c2UgYGdncGxvdDJgIHRvIHBsb3QgdGhlIGNvcnJlY3QgdmFyaWFibGVzIHRvIHRoZSB4IGFuZCB5IGF4ZXMuIEluIHRoaXMgY2FzZSwgd2UgYXJlIGludGVyZXN0ZWQgaW4gdXNpbmcgdGhlIGBsb2cyRm9sZENoYW5nZWAgYXMgb3VyIGBNYCBhbmQgYGJhc2VNZWFuYCByZXByZXNlbnRzIG91ciBgQWAuIFRoZXJlIGFyZSBhIG51bWJlciBvZiB3YXlzIHdlIGNhbiBjb2xvdXIgdGhpcyBwbG90IGJ1dCB3ZSdsbCB0YWtlIGludG8gYWNjb3VudCBvdXIgYHBhZGpgIHZhbHVlcyB0byBoaWdobGlnaHQgdGhvc2UgZ2VuZXMgd2l0aCBERSB2YWx1ZXMgdGhhdCBhcmUgc3RhdGlzdGljYWxseSBzaWduaWZpY2FudC4NCg0KTm93LCBpZiB3ZSB3ZXJlIHVzaW5nIHJhdyBvciBub3JtYWxpemVkIGV4cHJlc3Npb24gZGF0YSwgd2Ugd291bGQgbmVlZCB0byBjYWxjdWxhdGUgdGhlIHByb3BlciB2YWx1ZXMgZm9yIHRoZSB0cmFuc2Zvcm1hdGlvbiBvZiBgTWAgYW5kIGBBYC4gVGhlcmUgYXJlIGEgZmV3IHBhY2thZ2VzIHRoYXQgY2FuIHRha2UgY2FyZSBvZiB0aGlzIGZvciB5b3Ugc3VjaCBhcyBgZ2dtYXBsb3RgIHdoaWNoIHlvdSBjYW4gZmluZCBbaGVyZV0oaHR0cHM6Ly9ycGtncy5kYXRhbm92aWEuY29tL2dncHVici9yZWZlcmVuY2UvZ2dtYXBsb3QuaHRtbCkNCg0KRm9yIG91ciBwbG90LCB0byBkaWZmZXJlbnRpYXRlIGJldHdlZW4gb3VyIHBvaW50cywgbGV0J3MgdXNlIHRoZSBgZ2doaWdobGlnaHRgIHBhY2thZ2UgZnJvbSBsYXN0IHdlZWshIFdoYXQgd2lsbCBvdXIgcHJlZGljYXRlcyBiZSBiYXNlZCBvbj8gTG9nfjJ+IGZvbGQgY2hhbmdlPyBBZGp1c3RlZCBwLXZhbHVlPw0KDQpgYGB7ciwgZmlnLndpZHRoPTEwLCBmaWcuaGVpZ2h0PTEwfQ0KDQojIE1BIHBsb3QhIE5lZWQgdG8gc2VsZWN0IGJhc2VkIG9uIGEgZm9sZCBjaGFuZ2UgPiB4IGFuZCBwYWRqIDw9IGZkcg0KIyBVc3VhbGx5IEZDID0gMS41LCBmZHIgPSAwLjA1IGJ1dCB3ZSdsbCB1c2UgdGhlICJzdGF0dXMiIHZhcmlhYmxlIGhlcmUgaW5zdGVhZC4NCg0KREVfQTU0OV9JQVYuZGYgJT4lIA0KICBmaWx0ZXIoc3RhdHVzID09ICJPSyIpICU+JSANCg0KICAjIDEuIERhdGENCiAgZ2dwbG90KC4pICsNCiAgICAjIDIuIEFlc3RoZXRpY3MNCiAgICBhZXMoeD1iYXNlTWVhbiwgeSA9IGxvZzJGb2xkQ2hhbmdlKSArDQogIA0KICAgICMgVGhlbWUNCiAgICB0aGVtZV9idygpICsNCiAgICB0aGVtZSh0ZXh0ID0gZWxlbWVudF90ZXh0KHNpemU9MjApKSArDQogIA0KICAgICMgMy4gU2NhbGluZw0KICAgICMgVXNlIGEgbG9nMTAgc2NhbGUgb24gdGhlIHggYXhpcw0KICAgIHNjYWxlX3hfbG9nMTAoKSArIA0KICANCiAgICAjIFNldCB0aGUgYnJlYWtzIG9uIG91ciB5LWF4aXMgDQogICAgc2NhbGVfeV9jb250aW51b3VzKGxpbWl0cyA9IGMoLTEwLCAxMCksIGJyZWFrcyA9IHNlcSgtMTAsIDEwLCAyKSkgKw0KICANCiAgICAjIDQuIEdlb21zDQogICAgIyBDb2xvdXIgb3VyIHBvaW50cyBhbGwgaW4gcmVkDQogICAgZ2VvbV9wb2ludChjb2xvdXIgPSAicmVkIiwgYWxwaGEgPSAwLjIsIHNpemU9NCwgbmEucm09VFJVRSkgKw0KICANCiAgICAjIyMgMi4xLjINCiAgICAjIENoYW5nZSBpdCBzbyB0aGF0IG9ubHkgaGlnaCBMMkZDIHdpdGggbG93IHAtdmFsdWVzIHdpbGwgYmUgaW4gcmVkLCB0aGUgcmVzdCBhcyBibGFjaw0KICAgIGdnaGlnaGxpZ2h0KC4uLiwgLi4uLCB1bmhpZ2hsaWdodGVkX3BhcmFtcyA9IGxpc3QoY29sb3VyID0gImJsYWNrIikpDQpgYGANCg0KLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQoNCiMjIyAyLjEuMyBJbnRlcnByZXRpbmcgb3VyIE1BIHBsb3QNCg0KU28gb3VyIHBsb3Qgc2hvd3MgdXMgdGhlIGdlbmVyYWwgZGlzdHJpYnV0aW9uIG9mIHRoZSBERSBkYXRhLiBMb29raW5nIGF0IGl0LCB3ZSBjYW4gc2VlLCBub3RhYmx5LCB0aGF0IGl0IGFwcGVhcnMgdGhhdCB3ZSBoYXZlIG1hbnkgbW9yZSBnZW5lcyB1cHJlZ3VsYXRlZCB0aGFuIGRvd25yZWd1bGF0ZWQgaW4gY29tcGFyaXNvbiB0byBvdXIgY29udHJvbC4gV2UgYWxzbyBzZWUsIGFzIGV4cGVjdGVkLCBtb3JlIGxvdy1wcm9iYWJpbGl0eSB2YXJpYXRpb24gb2NjdXJyaW5nIHdpdGggbG93ZXIgbm9ybWFsaXplZCBtZWFuIGNvdW50cy4gQ29udmVyc2VseSwgYXMgb3VyIG92ZXJhbGwgbWVhbiBjb3VudHMgaW5jcmVhc2UsIHdlIGFsc28gc2VlIGxlc3MgdmFyaWF0aW9uIGluIGdlbmVyYWwgYnV0IHdlIGRvIHNlZSBhIGdlbmUgdGhhdCBkb2VzIHBhc3MgdGhyZXNob2xkIERFIHdpdGggYSBsb3cgZW5vdWdoIHAtdmFsdWUhIFRvIGludmVzdGlnYXRlIGZ1cnRoZXIgeW91IGNvdWxkIGZpbHRlciB0aGUgZGF0YSBvciBsb29rIGFnYWluIHdpdGggYSAqKnZvbGNhbm8gcGxvdCoqIQ0KDQotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCg0KIyMgMi4yLjAgVm9sY2FubyBwbG90cyBleGFtaW5lIGZvbGQtY2hhbmdlIHZzIHAtdmFsdWUNCg0KV2hpbGUgdGhlIE1BIHBsb3RzIGV4cGxvcmVkIGRhdGEgYmFzZWQgb24gaXRzIGZvbGQtY2hhbmdlIHZhbHVlcyBpbiByZWxhdGlvbiB0byBtZWFuIGV4cHJlc3Npb24sIHRoZSB2b2xjYW5vIHBsb3QgbG9va3MgcHVyZWx5IGF0IGRpZmZlcmVudGlhbCBleHByZXNzaW9uIGJhc2VkIG9uIGl0J3MgKipwLXZhbHVlKiogKGllIHRoZSBwcm9iYWJpbGl0eSB0aGF0IHRoZSBERSBpcyByZWFsKS4gVG8gZW1waGFzaXplIHRoZSBpbXBvcnRhbmNlIG9mIHRoZSBwLXZhbHVlIHdlIHdpbGwgKiotbG9nIHRyYW5zZm9ybSoqIGl0IHNvIHNtYWxsZXIgcC12YWx1ZXMgYXJlIGhpZ2hlciB1cCBvbiB0aGUgeS1heGlzLiBUaGlzIHF1aWNrbHkgcGFydGl0aW9ucyBkYXRhIHBvaW50cyBpbiB0aGUgdmlzdWFsaXphdGlvbiB0byBkcmF3IGhpZ2gtREUvbG93IHAtdmFsdWUgY29tYmluYXRpb25zIGF3YXkgZnJvbSB0aGUgYnVsayBvZiB0aGUgcG9wdWxhdGlvbi4NCg0KVG8gcGxvdCB0aGlzIGRhdGEsIHdlJ2xsIHJldHVybiB0byBvdXIgbGFyZ2VyIGBibGFuY29fZGF0YV9sb25nLmRmYCB3aGljaCBjb250YWlucyBkYXRhIGZyb20gbXVsdGlwbGUgZXhwZXJpbWVudHMuIEZvciBub3csIGxldCdzIGZvY3VzIG9uIGp1c3QgdGhlIFNBUlMtQ29WLTIgZGF0YSB0aGF0IGhhcyBiZWVuIGdlbmVyYXRlZCBpbiB0aGUgQTU0OSBob3N0IGNlbGxzLiBPdXIgKi1sb2cqIHRyYW5zZm9ybWF0aW9uIG9mIHRoZSBwLXZhbHVlcyB3aWxsIGFsc28gYmUgcHJvYmxlbWF0aWMgd2hlbiBlbmNvdW50ZXJpbmcgMCwgc28gYmUgY2FyZWZ1bCEgTGV0J3MgY29tcGFyZSBwbG90cyB3aXRoIGFuZCB3aXRob3V0IHRoZXNlIHByb2JsZW0gdmFsdWVzLg0KDQpUbyBsYWJlbCBvdXIgcG9pbnRzIG9mIGludGVyZXN0LCB3ZSdsbCBhbHNvIHVzZSB0aGUgYGdlb21fdGV4dF9yZXBlbCgpYCBmcm9tIHRoZSBgZ2dyZXBlbGAgcGFja2FnZS4gVGhpcyB3aWxsIGhlbHAgYXJyYW5nZSBhbmQgcGxvdCB0ZXh0IG9udG8gb3VyIHNjYXR0ZXJwbG90IGluIGEgd2F5IHRoYXQgYXZvaWRzIGNvbGxpc2lvbnMgd2l0aCB0aGUgZGF0YSBhbmQgdGhlIG90aGVyIGxhYmVscyEgWW91IGNvdWxkIGFsc28gZXhwZXJpbWVudCB3aXRoIHVzaW5nIHRoZSBgZGlyZWN0bGFiZWxzYCBwYWNrYWdlIHRoYXQgd2UgcGxheWVkIGFyb3VuZCB3aXRoIGxhc3Qgd2VlayB0byBhY2NvbXBsaXNoIGEgc2ltaWxhciBmZWF0Lg0KDQpgYGB7cn0NCiMgUmVtaW5kIG91cnNlbHZlcyB3aGF0IG91ciBkYXRhIHRhYmxlIGxvb2tzIGxpa2UNCmhlYWQoYmxhbmNvX2RhdGFfbG9uZy5kZikNCmBgYA0KDQpgYGB7ciwgZmlnLndpZHRoPTIwLCBmaWcuaGVpZ2h0PTEwfQ0KDQojIFJlbW92ZSBwLXZhbHVlcyA9IDANCnZvbGNhbm8ucGxvdDEgPC0NCg0KICAjIFZvbGNhbm8gcGxvdCENCiAgYmxhbmNvX2RhdGFfbG9uZy5kZiAlPiUgDQogIGZpbHRlcihwYXRob2dlbiA9PSAiU0FSUy1Db1YtMiIsIGhvc3Q9PSJBNTQ5IiwgcGFkaiAhPTApICU+JSANCiAgDQogICMgMS4gRGF0YQ0KICBnZ3Bsb3QoLikgKw0KICAgICMgMi4gQWVzdGhldGljcw0KICAgIGFlcyh4PUwyRkMsIHkgPSAtbG9nMTAocGFkaikpICsNCiAgDQogICAgIyBUaGVtZXMNCiAgICB0aGVtZV9idygpKw0KICAgIHRoZW1lKHRleHQgPSBlbGVtZW50X3RleHQoc2l6ZT0yMCkpICsNCiAgICBsYWJzKHRpdGxlID0gIlZvbGNhbm8gcGxvdCB3aXRoIHAtdmFsdWVzID09IDAgcmVtb3ZlZCIpICsNCiAgDQogICAgIyA0LiBHZW9tcw0KICAgICMgcG9pbnRzIGFsbCBjb2xvdXJlZCByZWQNCiAgICBnZW9tX3BvaW50KGNvbG91ciA9ICJyZWQiLCBhbHBoYSA9IDAuNywgc2l6ZT00KSArDQogIA0KICAgICMgaGlnaGxpZ2h0IGxlYXZlcyBvbmx5IHRoZSBvbmVzIG1lZXRpbmcgb3VyIGNyaXRlcmlhIGFzIHJlZCwgY29sb3VyIHJlc3QgYXMgYmx1ZQ0KICAgIGdnaGlnaGxpZ2h0KGFicyhMMkZDKSA+IDIsIHBhZGogPCAwLjA1LCB1bmhpZ2hsaWdodGVkX3BhcmFtcyA9IGxpc3QoY29sb3VyID0gImxpZ2h0Ymx1ZSIpKSArDQoNCiAgICAjIyMgMi4yLjANCiAgICAjIEFkZCBsYWJlbHMgYnV0IG9ubHkgZm9yIHRoZSB0b3AgMTAgZ2VuZXMNCiAgICBnZW9tX3RleHRfcmVwZWwoZGF0YSA9IGJsYW5jb19kYXRhX2xvbmcuZGYgJT4lDQogICAgICAgICAgICAgICAgICAgICAgICAjIEZpbHRlciB0aGUgZGF0YSB1c2VkIHRvIGxhYmVsDQogICAgICAgICAgICAgICAgICAgICAgICBmaWx0ZXIoLi4uID09ICJTQVJTLUNvVi0yIiwgLi4uPT0iQTU0OSIsIGFicyhMMkZDKSA+IDIsIHBhZGogIT0wKSAlPiUNCiAgICAgICAgICAgICAgICAgICAgICAgICMgQXJyYW5nZSBpbiBhc2NlbmRpbmcgb3JkZXINCiAgICAgICAgICAgICAgICAgICAgICAgIGFycmFuZ2UocGFkaikgJT4lDQogICAgICAgICAgICAgICAgICAgICAgICAjIFRha2UgdGhlIGZpcnN0IDEwIHJlc3VsdHMNCiAgICAgICAgICAgICAgICAgICAgICAgIGRwbHlyOjpzbGljZSgxOjEwKSwNCiAgICAgICAgICAgICAgICAgICAgYWVzKGxhYmVsID0gLi4uKSwgc2l6ZT02KQ0KYGBgDQoNCmBgYHtyLCBmaWcud2lkdGg9MjAsIGZpZy5oZWlnaHQ9MTB9DQoNCiMgbGVhdmUgaW4gcC12YWx1ZXMgPSAwDQp2b2xjYW5vLnBsb3QyIDwtDQoNCiAgIyBWb2xjYW5vIHBsb3QhDQogIGJsYW5jb19kYXRhX2xvbmcuZGYgJT4lIA0KICBmaWx0ZXIocGF0aG9nZW4gPT0gIlNBUlMtQ29WLTIiLCBob3N0PT0iQTU0OSIpICU+JSANCiAgDQogICMgMS4gRGF0YQ0KICBnZ3Bsb3QoLikgKw0KICAgICMgMi4gQWVzdGhldGljcw0KICAgIGFlcyh4PUwyRkMsIHkgPSAtbG9nMTAocGFkaikpICsNCg0KICAgICMgVGhlbWVzDQogICAgdGhlbWVfYncoKSsNCiAgICB0aGVtZSh0ZXh0ID0gZWxlbWVudF90ZXh0KHNpemU9MjApKSArDQogICAgbGFicyh0aXRsZSA9ICJWb2xjYW5vIHBsb3Qgd2l0aCBhbGwgcC12YWx1ZXMiKSArDQoNCiAgICAjIDQuIEdlb21zDQogICAgIyBwb2ludHMgYWxsIGNvbG91cmVkIHJlZA0KICAgIGdlb21fcG9pbnQoY29sb3VyID0gInJlZCIsIGFscGhhID0gMC43LCBzaXplPTQpICsNCg0KICAgICMgaGlnaGxpZ2h0IGxlYXZlcyBvbmx5IHRoZSBvbmVzIG1lZXRpbmcgb3VyIGNyaXRlcmlhIGFzIHJlZCwgY29sb3VyIHJlc3QgYXMgYmx1ZQ0KICAgIGdnaGlnaGxpZ2h0KGFicyhMMkZDKSA+IDIsIHBhZGogPCAwLjA1LCB1bmhpZ2hsaWdodGVkX3BhcmFtcyA9IGxpc3QoY29sb3VyID0gImxpZ2h0Ymx1ZSIpKSArDQoNCiAgICAjIEFkZCBsYWJlbHMgYnV0IG9ubHkgZm9yIHRoZSB0b3AgMTAgZ2VuZXMNCiAgICBnZW9tX3RleHRfcmVwZWwoZGF0YSA9IGJsYW5jb19kYXRhX2xvbmcuZGYgJT4lIA0KICAgICAgICAgICAgICAgICAgICAgICAgIyBGaWx0ZXIgdGhlIGRhdGEgdXNlZCB0byBsYWJlbA0KICAgICAgICAgICAgICAgICAgICAgICAgZmlsdGVyKHBhdGhvZ2VuID09ICJTQVJTLUNvVi0yIiwgaG9zdD09IkE1NDkiLCBhYnMoTDJGQykgPiAyKSAlPiUgDQogICAgICAgICAgICAgICAgICAgICAgICAjIEFycmFuZ2UgaW4gYXNjZW5kaW5nIG9yZGVyDQogICAgICAgICAgICAgICAgICAgICAgICBhcnJhbmdlKHBhZGopICU+JSANCiAgICAgICAgICAgICAgICAgICAgICAgICMgVGFrZSB0aGUgZmlyc3QgMTAgcmVzdWx0cw0KICAgICAgICAgICAgICAgICAgICAgICAgZHBseXI6OnNsaWNlKDE6MTApLA0KICAgICAgICAgICAgICAgICAgICBhZXMobGFiZWwgPSBHZW5lTmFtZSksIHNpemU9NikNCmBgYA0KDQpgYGB7ciwgZmlnLndpZHRoPTIwLCBmaWcuaGVpZ2h0PTEwfQ0KIyBQbG90IGJvdGggb2Ygb3VyIGZpZ3VyZXMgdG9nZXRoZXINCmZpZy5zaG93PSJob2xkIjsgb3V0LndpZHRoPSI1MCUiDQp2b2xjYW5vLnBsb3QxDQp2b2xjYW5vLnBsb3QyDQpgYGANCg0KLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQoNCiMjIyAyLjIuMSBJbnRlcnByZXRpbmcgeW91ciB2b2xjYW5vIHBsb3RzDQoNCkxvb2tpbmcgYXQgb3VyIHZvbGNhbm8gcGxvdHMsIHdlIGNhbiBjbGVhcmx5IHNlZSB0aGUgdm9sY2FubyBzaGFwZSB3ZSBhcmUgbG9va2luZyBmb3IuIFRoZSBkYXRhIGlzIHNwbGl0IGJldHdlZW4gb3VyICRccG0kIGxvZyRfezJ9JCBmb2xkIGNoYW5nZXMgd2l0aCBwb2ludHMgaGlnaGxpZ2h0ZWQgd2hlbiB3ZSBzZWUgREUgYmV5b25kIHRoaXMuIFlvdSBjYW4gY3VzdG9taXplIHlvdXIgcGFyYW1ldGVycyBidXQgeW91IGNhbiBhbHNvIHNlZSB0aGUgZW1waGFzaXMgb24gbG93ZXIgcC12YWx1ZXMgaW4gb3VyIGRhdGEgc2V0LiBUaGUgbW9zdCAic2lnbmlmaWNhbnQiIGRhdGEgaXMgZm91bmQgaW4gdGhlIHVwcGVyIGxlZnQgYW5kIHJpZ2h0IHF1YWRyYW50cyBvZiB0aGUgZ3JhcGgsIHdoaWNoIHdhc24ndCBtYWRlIGNsZWFyIGluIHRoZSBNQSBwbG90LiBZb3UgY291bGQgdXNlIHRoZSBzYW1lIHRyaWNrcyB0byBsYWJlbCB5b3VyIE1BIHBsb3QgdG9vIGJ1dCB0aGUgZGF0YSBjb3VsZCBlbmQgdXAgYW55d2hlcmUgYWxvbmcgdGhlIE1BIHBsb3QuDQoNCkluIHRoZSBlbmQsIGlmIHlvdSAqZG8qIGhhdmUgYWNjZXNzIHRvIHRoZSBvcmlnaW5hbCBjb3VudHMsIGl0IG1heSBiZSBvZiB1c2UgdG8gY29uc2lkZXIgdGhpcyBpbmZvcm1hdGlvbiB3aGVuIGZ1cnRoZXIgaW52ZXN0aWdhdGluZyB5b3VyIGNhbmRpZGF0ZSBnZW5lcyBmcm9tIERFLg0KDQo6Ojogey5hbGVydCAuYWxlcnQtYmxvY2sgLmFsZXJ0LWRhbmdlcn0NCioqU2VjdGlvbiAyLjAuMCBDb21wcmVoZW5zaW9uIFF1ZXN0aW9uOioqIENvbXBhcmluZyBvdXIgdHdvIHZlcnNpb25zIG9mIHRoZSB2b2xjYW5vIHBsb3QsIHdlIHNlZSBkaWZmZXJlbnQgc2V0cyBvZiBnZW5lcyBoaWdobGlnaHRlZCBhcyB0aGUgInRvcCAxMCIgYmFzZWQgb24gYWRqdXN0ZWQgcC12YWx1ZXMuIFdoYXQgbWFrZXMgdGhlc2UgdHdvIHNldHMgb2YgZGF0YSBkaWZmZXJlbnQ/IFdoeSBkbyB3ZSBzZWUgZGF0YXBvaW50cyBhdCB0aGUgdG9wIGVkZ2Ugb2Ygb3VyIHBsb3RzIGluIHRoZSBzZWNvbmQgdmVyc2lvbj8NCjo6Og0KDQotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCg0KIyAzLjAuMCBNZWRpdW0gcmVzb2x1dGlvbiBhbmFseXNpcyBmb2N1c2VzIG9uIGdyb3VwcyBvZiBnZW5lcw0KDQpOb3cgdGhhdCB3ZSd2ZSB0YWtlbiBhbiBvdmVyYWxsIGxvb2sgYXQgb3VyIGRhdGEsIHdlIGNhbiBiZWdpbiB0byBhc3Nlc3Mgb3VyIGRhdGEgYmFzZWQgb24gc29tZSBjYW5kaWRhdGUgZ2VuZXMgb3IgaHlwb3RoZXNpcyB0ZXN0aW5nLiBGb3IgaW5zdGFuY2UsIHlvdSBtYXkgb25seSBiZSBpbnRlcmVzdGVkIGluIGxvb2tpbmcgYXQgZ2VuZXMgd2l0aCBhbiBMMkZDIFw+IDMuIEluIG90aGVyIGNhc2VzLCB5b3UgbWF5IGJlIGludGVyZXN0ZWQgaW4gYSBncm91cCBvZiBnZW5lcyBwZXJ0YWluaW5nIHRvIGEgZnVuY3Rpb24gbGlrZSB0aGUgY2hlbW9raW5lcyB3aGljaCBhcmUgcGFydCBvZiB0aGUgaW5mbGFtbWF0aW9uIHJlc3BvbnNlLg0KDQpBZnRlciBzZWxlY3RpbmcgYSBzdWJzZXQgb2YgZ2VuZXMgd2UgY2FuIGJlZ2luIHRvIGNvbXBhcmUgdGhlaXIgREUgZGF0YSBtb3JlIG1lYW5pbmdmdWxseSBhY3Jvc3MgZGlmZmVyZW50IHNhbXBsZSBzZXRzLiBMZXQncyBjb250aW51ZSB3b3JraW5nIHdpdGggb3VyIGxvbmctZm9ybWF0IGRhdGFzZXQgYGJsYW5jb19kYXRhX2xvbmcuZGZgIHdoaWNoIGNvbnRhaW5zIG91ciBtdWx0aXBsZSBSTkFzZXEgZXhwZXJpbWVudHMgZnJvbSB2YXJpb3VzIGluZmVjdGlvbiBjb25kaXRpb25zLiBXZSB3aWxsIGJlZ2luIGJ5IGdlbmVyYXRpbmcgYSBsaXN0IG9mIHRoZSBoaWdoZXN0IERFIGdlbmVzIHdpdGggbG93IHAtdmFsdWVzIGluIHRoZSBTQVJTLUNvVi0yIGluZmVjdGlvbiBzY2VuYXJpby4NCg0KTm90ZSBoZXJlIHdlJ2xsIHVzZSB0aGUgYHB1bGwoKWAgZnVuY3Rpb24gd2hpY2ggaXMgYSBuaWNlci1sb29raW5nIHdheSB0byByZXRyaWV2ZSBhICpzaW5nbGUqIGNvbHVtbiBhcyBhIHZlY3RvciBmcm9tIG91ciBgdGliYmxlYCBvYmplY3QuIFRoaXMgaXMgZXF1aXZhbGVudCB0byB1c2luZyBhIGAuJGNvbE5hbWVgIGZvcm1hdC4NCg0KYGBge3J9DQojIFNlbGVjdCB0aGUgZ2VuZXMgd2Ugd2FudCB0byB2aXN1YWxpemUNCmhlYXRtYXBfZ2VuZS5saXN0IDwtIA0KICBibGFuY29fZGF0YV9sb25nLmRmICU+JSANCiAgDQogICMgZmlsdGVyIGZvciBkYXRhIGZyb20gYSBzcGVjaWZpYyBleHBlcmltZW50LCBhbmQgdGhlbiBieSBoaWdoIHBvc2l0aXZlIEwyRkMgdmFsdWVzIHdpdGggbG93IHAtdmFsdWVzDQogIGZpbHRlcihwYXRob2dlbiA9PSAiU0FSUy1Db1YtMiIsIGhvc3QgPT0gIkE1NDkiLCBMMkZDID4gLi4uLCBwYWRqIDwgLi4uKSAlPiUgDQogIC4uLg0KICANCiAgIyBUYWtlIGEgbG9vayBhdCB0aGUgcmVzdWx0aW5nIGxpc3QNCiAgaGVhdG1hcF9nZW5lLmxpc3QNCmBgYA0KDQotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCg0KIyMgMy4xLjAgVXNlIGhlYXRtYXBzIHRvIHZpc3VhbGl6ZSBkYXRhIGFjcm9zcyBhIGNvbnRpbnVvdXMgcmFuZ2Ugb2YgdmFsdWVzDQoNCk5vdyB0aGF0IHdlIGhhdmUgZ2VuZXJhdGVkIGEgc2VsZWN0IGdyb3VwIG9mIGdlbmVzIHRvIGV4YW1pbmUsIG91ciBERSBkYXRhIGlzIHdlbGwtc3VpdGVkIHRvIHZpc3VhbGl6aW5nIGluIGhlYXRtYXAgb3IgaGVhdCBwbG90LiBJbiBvdXIgaGVhdHBsb3Qgd2UgY2FuIGdlbmVyYXRlIHZhbHVlcyBhbG9uZyB0d28gY2F0ZWdvcmljYWwgYXhlcy4gT24gdGhlIHktYXhpcyB3ZSBjYW4gcGxvdCBERSB2YWx1ZXMgZnJvbSBvdXIgaW5kaXZpZHVhbCBnZW5lcywgd2hpbGUgb24gdGhlIHgtYXhpcyB3ZSB3aWxsIHNob3cgdmFsdWVzIGFjcm9zcyBtdWx0aXBsZSBleHBlcmltZW50cy4gVG8gdmlzdWFsaXplIHRoZSBkYXRhIGl0c2VsZiwgd2Ugd2lsbCB1c2UgdGhlIGBnZW9tX3RpbGUoKWAgY29tbWFuZC4NCg0KYGBge3IsIGZpZy53aWR0aD0xMiwgZmlnLmhlaWdodD0xMH0NCg0KIyBoZWF0bWFwIG9mIG91ciBkYXRhIGJhc2VkIG9uIGEgbGlzdCBvZiB0b3AgREUgZ2VuZXMgZnJvbSB0aGUgU0FSUy1Db1YtMiBpbmZlY3Rpb24gb2YgQTU0OSBjZWxscw0KDQpibGFuY29fZGF0YV9sb25nLmRmICU+JSANCiAgZmlsdGVyKEdlbmVOYW1lICVpbiUgaGVhdG1hcF9nZW5lLmxpc3QsDQogICAgICAgICAgIGhvc3QgPT0gIkE1NDkiDQogICAgICAgICkgJT4lIA0KICAjIG11dGF0ZShHZW5lTmFtZSA9IGZhY3RvcihHZW5lTmFtZSwgbGV2ZWxzID0gaGVhdG1hcF9nZW5lLmxpc3QpKSAlPiUgDQogIA0KICAjIDEuIERhdGENCiAgZ2dwbG90KC4pICsNCiAgICAjMi4gQWVzdGhldGljcw0KICAgICMjIyAzLjEuMCBzZXQgYWVzdGhldGljcw0KICAgIGFlcyh4PS4uLiwgeSA9IC4uLiwgZmlsbD0uLi4pICsNCg0KICAgICMgVGhlbWUNCiAgICB0aGVtZV9idygpKw0KICAgIHRoZW1lKHRleHQgPSBlbGVtZW50X3RleHQoc2l6ZT0xOCkNCiAgICAgICAgICkrDQogICAgbGFicyh0aXRsZSA9ICJIZWF0bWFwIG9mIEwyRkMgdmFsdWVzIGluIEE1NDkgY2VsbHMgaW5mZWN0ZWQgYnkgZGlmZmVyZW50IHBhdGhvZ2VucyIpICsNCg0KICAgICMgMy4gU2NhbGluZw0KICAgICMgS2VlcCB0aGlzIHRvIHJldmVyc2UgdGhlIHkgYXhpcyBvciBwdXQgaXQgaW50byB0aGUgYXNzaWdubWVudD8NCiAgICBzY2FsZV95X2Rpc2NyZXRlKGxpbWl0cyA9IHJldikgKyANCiAgICBzY2FsZV9maWxsX3ZpcmlkaXNfYyhvcHRpb249InBsYXNtYSIpICsNCg0KICAgICMgR2VvbXMNCiAgICAjIyMgMy4xLjAgVXNlIGEgdGlsZSBhbmQgc2V0IHRoZSB3aWR0aCBhIGxpdHRsZSBzbWFsbGVyIHRvIGdpdmUgYSBnYXAgYmV0d2VlbiBncm91cHMNCiAgICBnZW9tX3RpbGUoLi4uKQ0KYGBgDQoNCmBgYHtyLCBmaWcud2lkdGg9MTIsIGZpZy5oZWlnaHQ9MTR9DQoNCiMgaGVhdG1hcCBvZiBvdXIgZGF0YSBiYXNlZCBvbiBhIGxpc3Qgb2YgdG9wIERFIGdlbmVzIGZyb20gdGhlIFNBUlMtQ29WLTIgaW5mZWN0aW9uIG9mIEE1NDkgY2VsbHMNCiMgU3BsaXQgeC1heGlzIGJ5IGV4cGVyaW1lbnQgbmFtZQ0KDQpibGFuY29fZGF0YV9sb25nLmRmICU+JSANCiAgZmlsdGVyKEdlbmVOYW1lICVpbiUgaGVhdG1hcF9nZW5lLmxpc3QsDQogICAgICAgICAgIC4uLg0KICAgICAgICApICU+JSANCiAgDQogICMgMS4gRGF0YQ0KICBnZ3Bsb3QoLikgKw0KICAgICMyLiBBZXN0aGV0aWNzDQogICAgYWVzKHg9ZXhwZXJpbWVudCwgeSA9IEdlbmVOYW1lLCBmaWxsPUwyRkMpICsNCg0KICAgICMgVGhlbWUNCiAgICB0aGVtZV9idygpKw0KICAgIHRoZW1lKHRleHQgPSBlbGVtZW50X3RleHQoc2l6ZT0xOCksDQogICAgICAgICAgYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGU9NDUsIGhqdXN0ID0gMSkNCiAgICAgICAgICkgKw0KICAgIGxhYnModGl0bGUgPSAiSGVhdG1hcCBvZiBMMkZDIHZhbHVlcyBpbiBTQVJTLUNvVjIgaW5mZWN0aW9ucyBieSBleHBlcmltZW50IikgKw0KDQogICAgIyAzLiBTY2FsaW5nDQogICAgIyBLZWVwIHRoaXMgdG8gcmV2ZXJzZSB0aGUgeSBheGlzIG9yIHB1dCBpdCBpbnRvIHRoZSBhc3NpZ25tZW50Pw0KICAgIHNjYWxlX3lfZGlzY3JldGUobGltaXRzID0gcmV2KSArIA0KICAgIHNjYWxlX2ZpbGxfdmlyaWRpc19jKG9wdGlvbj0icGxhc21hIikgKw0KDQogICAgIyBHZW9tcw0KICAgICMgVXNlIGEgdGlsZSBhbmQgc2V0IHRoZSB3aWR0aCBhIGxpdHRsZSBzbWFsbGVyIHRvIGdpdmUgYSBnYXAgYmV0d2VlbiBncm91cHMNCiAgICBnZW9tX3RpbGUod2lkdGggPSAwLjk1KQ0KYGBgDQoNCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KDQojIyMgMy4xLjEgSW50ZXJwcmV0aW5nIGEgaGVhdG1hcCBvZiB1cHBlciB2YWx1ZXMNCg0KRnJvbSBvdXIgbGlzdCBvZiB0aGUgdG9wIDE4IERFIGhpdHMgaW4gb3VyIFNBUlMtQ29WLTIgc2V0LCBpdCBsb29rcyBsaWtlIHdlIHNlZSBzb21lIHNpbWlsYXIgaGl0cyBhY3Jvc3Mgc29tZSBvZiB0aGUgZ2VuZXMgd2hlbiBpbmZlY3RpbmcgQTU0OSBjZWxscyB3aXRoIG90aGVyIHZpcnVzZXMgbGlrZSBIUElWMyBhbmQgUlNWLg0KDQpXZSBzZWUgc3Ryb25nIEwyRkMgdmFsdWVzIGZvciBJTDYsIElMMUEgYW5kIExBTUMyIGZvciBpbnN0YW5jZS4gSUw2IChpbnRlcmxldWtpbiA2KSBoYXMgYmVlbiByZXBvcnRlZCB0byBwbGF5IGEgcm9sZSBpbiBib3RoIG1vdW50aW5nIGFuIGVmZmVjdGl2ZSBpbW11bmUgcmVzcG9uc2UgdG8gY2VydGFpbiB2aXJhbCBpbmZlY3Rpb25zIGJ1dCBpdHMgaW50ZXJhY3Rpb25zIHdpdGggb3RoZXIgZmFjdG9ycyBtYXkgYWxzbyBoYXZlIGEgcG90ZW50aWFsIHJvbGUgaW4gdGhlIGV4YWNlcmJhdGlvbiBvZiB2aXJhbCBwaGVub3R5cGVzLiBJbiBsb29raW5nIGFjcm9zcyBkaWZmZXJlbnQgaG9zdCBjZWxsIGluZmVjdGlvbnMgYnkgU0FSUy1Db1YtMiB0aGVyZSBpcyBhZ2FpbiBjb25zaXN0ZW50IHVwcmVndWxhdGlvbiBvZiBJTDFBIGFuZCBJTDYuDQoNClVzaW5nIGEgc2ltaWxhciBtZXRob2QsIHdlIGNhbiBpbnN0ZWFkIGZvY3VzIG9uIGdlbmVzIGZyb20gYSBzcGVjaWZpYyBmYW1pbHkgb3IgcGF0aHdheSBhcyBsb25nIGFzIHdlIGhhdmUgYSBsaXN0LiBMZXQncyB0cnkgdGhlIGZhbWlseSBvZiBjaGVtb2tpbmVzIHdoaWNoIHBsYXkgYSByb2xlIGluIGluZHVjaW5nIGNlbGwgbW92ZW1lbnQgZHVyaW5nIHRoZSBpbW11bmUgcmVwb25zZS4NCg0KYGBge3J9DQojIEJ1aWxkIGEgbGlzdCBvZiB0aGUgcG90ZW50aWFsIGNoZW1va2luZXMgYnkgbG9va2luZyBmb3IgdGhvc2UgbWF0Y2hpbmcgdGhlICJDQ0wiIHBhdHRlcm4NCmhlYXRtYXBfY2NsLmxpc3QgPC0gDQogIGJsYW5jb19kYXRhX2xvbmcuZGYgJT4lIA0KICANCiAgIyBGaWx0ZXIgc3BlY2lmaWNhbGx5IGZvciBhbnkgZ2VuZXMgdGhhdCBoYXZlIENDTCBpbiB0aGVpciBuYW1lDQogIGZpbHRlciguLi4pICU+JSANCiAgcHVsbChHZW5lTmFtZSkgJT4lIA0KICB1bmlxdWUoKQ0KDQpzdHIoaGVhdG1hcF9jY2wubGlzdCkNCmBgYA0KDQotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCg0KTm93IHdlIGNhbiB1c2UgdGhpcyBsaXN0IGluIG91ciBgZ2dwbG90YCB2aXN1YWxpemF0aW9uLiBOb3RlIHRoYXQgd2UgKmNvdWxkKiBoYXZlIGdlbmVyYXRlZCBhIG11Y2ggbG9uZ2VyIHBpcGUgc2VyaWVzIHdpdGhvdXQgc2F2aW5nIHRoZSBvYmplY3QgYGhlYXRtYXBfY2NsLmxpc3RgIGJ1dCB0aGVuIHdlIHdvdWxkbid0IGhhdmUgYSBjaGFuY2UgdG8gZXhhbWluZSBpdCBiZWZvcmUgcGxvdHRpbmcgdGhhdCBkYXRhIHRvIGxlYXJuIHRoYXQgaXQgaGFzIDI5IHVuaXF1ZSB2YWx1ZXMuDQoNCmBgYHtyLCBmaWcud2lkdGg9MTIsIGZpZy5oZWlnaHQ9MTB9DQoNCiMgaGVhdG1hcCBvZiBvdXIgZGF0YSBiYXNlZCBvbiBhIGxpc3Qgb2YgY2hlbW9raW5lIGdlbmVzDQoNCmJsYW5jb19kYXRhX2xvbmcuZGYgJT4lIA0KDQogICMgRmlsdGVyIGZvciBnZW5lcyBpbiBvdXIgbGlzdCwgYW5kIGV4cGVyaW1lbnRzIHdoZXJlIHRoZSBob3N0IGNlbGwgd2FzICJBNTQ5Ig0KICBmaWx0ZXIoR2VuZU5hbWUgJWluJSAuLi4sDQogICAgICAgICAgIGhvc3QgPT0gIkE1NDkiDQogICAgICAgICkgJT4lIA0KICANCiAgIyAxLiBEYXRhDQogIGdncGxvdCguKSArDQogICAgIzIuIEFlc3RoZXRpY3MNCiAgICBhZXMoeD1leHBlcmltZW50LCB5ID0gR2VuZU5hbWUsIGZpbGw9TDJGQykgKw0KDQogICAgIyBUaGVtZQ0KICAgIHRoZW1lX2J3KCkrDQogICAgdGhlbWUodGV4dCA9IGVsZW1lbnRfdGV4dChzaXplPTIwKSwNCiAgICAgICAgICBheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZT00NSwgaGp1c3QgPSAxKQ0KICAgICAgICAgKSsNCg0KICAgICMgMy4gU2NhbGluZw0KICAgIHNjYWxlX2ZpbGxfdmlyaWRpc19jKG9wdGlvbj0icGxhc21hIikrDQoNCiAgICAjIEdlb21zDQogICAgIyBVc2UgYSB0aWxlIGFuZCBzZXQgdGhlIHdpZHRoIGEgbGl0dGxlIHNtYWxsZXIgdG8gZ2l2ZSBhIGdhcCBiZXR3ZWVuIGdyb3Vwcw0KICAgIGdlb21fdGlsZSh3aWR0aCA9IDAuOTUpDQpgYGANCg0KLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQoNCiMjIyAzLjEuMiBIZWF0bWFwIGludGVycHJldGF0aW9uDQoNCkZyb20gb3VyIGhlYXRtYXAsIHdlIGNhbiBvYnNlcnZlIHRoYXQgbG9va2luZyBvbmx5IGF0IHRoZSBjaGVtb2tpbmVzLCB0aGVyZSBpcyBjb25zaXN0ZW50IHVwcmVndWxhdGlvbiBvZiBDQ0w1IGFuZCBDQ0wyIHdoZW4gaW5mZWN0aW5nIG91ciBBNDU5IGNlbGxzIHdpdGggYW55IG9mIHRoZXNlIHZpcnVzZXMuIEluIGNvbXBhcmlzb24sIGhvd2V2ZXIsIEhQSVYzIGFuZCBSU1YgaW5mZWN0aW9uIHNob3cgYSBtdWNoIGhpZ2hlciBERSBhY3Jvc3MgdGhlIENDTCBmYW1pbHkgaW4gY29tcGFyaXNvbiB0byBTQVJTLUNvVi0yIHdoaWNoIGFwcGVhcnMgdG8gaWxsaWNpdCBsZXNzIGluZmxhbW1hdG9yeSByZXNwb25zZSBpbiB0aGUgdGltZS1mcmFtZSBzYW1wbGVkLg0KDQo6Ojogey5hbGVydCAuYWxlcnQtYmxvY2sgLmFsZXJ0LWluZm99DQoqKkJ1dCB3YWl0IHRoZXJlJ3MgbW9yZSEqKiBXaGlsZSB3ZSBhcmUgZGVhbGluZyB3aXRoIGp1c3QgYSBzdWJzZXQgb2Ygb3VyIFJOQS1TZXEgZGF0YSwgbXVjaCBtb3JlIGlzIGF2YWlsYWJsZSB0byB2aXN1YWxpemUgb24gYSBncmVhdGVyIHNjYWxlLiBOZXh0IHdlZWsgd2UnbGwgZGlnIGRlZXBlciBpbnRvIGhpZ2gtZGltZW5zaW9uYWwgZGF0YSBhbmQgaG93IGhlYXRtYXBzIGNhbiBiZSB1c2VkIHRvIHZpc3VhbGl6ZSBhbmQgb3JnYW5pemUgdGhpcyBraW5kIG9mIGluZm9ybWF0aW9uIHRvIGhlbHAgaWRlbnRpZnkgYW5kIGNvbnZleSBwYXR0ZXJucy4NCjo6Og0KDQotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCg0KIyMgMy4yLjAgTG9va2luZyBhdCBnZW5lcyB3aXRoIGRvdCBwbG90cw0KDQpOb3RlIHRoYXQgaW4gdGhlIGFib3ZlIGhlYXRtYXAgd2Ugbm8gbG9uZ2VyIGhhdmUgYW55IGluZm9ybWF0aW9uIG9uIHRoZSBhZGp1c3RlZCBwLXZhbHVlcyBmcm9tIHRoZXNlIEwyRkMgc2NvcmVzLiBTb21ldGhpbmcgdG8ga2VlcCBpbiBtaW5kIHdoZW4gbWFraW5nIHRoZXNlIGhlYXRtYXBzLiBXaGF0IHdvdWxkIGhhcHBlbiBpZiB3ZSBmaWx0ZXJlZCBvbiBwLXZhbHVlcyBhcyB3ZWxsPyBJcyB0aGVyZSBhIHdheSB3ZSBjb3VsZCB0cmFjayB0aGF0IGluZm9ybWF0aW9uPw0KDQpJZiB3ZSByZXBsYWNlIG91ciBgZ2VvbV90aWxlKClgIHdpdGggYSBgZ2VvbV9wb2ludCgpYCB3ZSBhcmUgYWJsZSB0byBhZGQgYW4gYWRkaXRpb25hbCBkaW1lbnNpb24gdG8gb3VyIGRhdGEgYnkgdXRpbGl6aW5nIGBzaXplYCBtYXBwaW5nLg0KDQpXZSdsbCByZXBlYXQgb3VyIHZpc3VhbGl6YXRpb24gZnJvbSBhYm92ZSBidXQgdXNlIGNvbG91ciB0byByZXByZXNlbnQgdGhlIGBMMkZDYCB2YWx1ZXMgKmFuZCogYSBzaXplIHRvIHJlcHJlc2VudCB0aGUgYHBhZGpgIHZhbHVlcyBpbiBvdXIgZGF0YXNldC4NCg0KYGBge3IsIGZpZy53aWR0aD0xMiwgZmlnLmhlaWdodD0xMH0NCg0KIyBoZWF0bWFwIG9mIG91ciBkYXRhIGJhc2VkIG9uIGEgbGlzdCBvZiBjaGVtb2tpbmUgZ2VuZXMNCg0KYmxhbmNvX2RhdGFfbG9uZy5kZiAlPiUgDQoNCiAgIyBGaWx0ZXIgZm9yIGdlbmVzIGluIG91ciBsaXN0LCBhbmQgZXhwZXJpbWVudHMgd2hlcmUgdGhlIGhvc3QgY2VsbCB3YXMgIkE1NDkiDQogIGZpbHRlcihHZW5lTmFtZSAlaW4lIGhlYXRtYXBfY2NsLmxpc3QsDQogICAgICAgICAgIGhvc3QgPT0gIkE1NDkiDQogICAgICAgICkgJT4lIA0KICANCiAgIyAxLiBEYXRhDQogIGdncGxvdCguKSArDQogICAgIzIuIEFlc3RoZXRpY3MNCiAgICBhZXMoeD1leHBlcmltZW50LCB5ID0gR2VuZU5hbWUpICsNCg0KICAgICMgVGhlbWUNCiAgICB0aGVtZV9idygpKw0KICAgIHRoZW1lKHRleHQgPSBlbGVtZW50X3RleHQoc2l6ZT0yMCksDQogICAgICAgICAgYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGU9NDUsIGhqdXN0ID0gMSkNCiAgICAgICAgICkrDQoNCiAgICAjIDMuIFNjYWxpbmcNCiAgICBzY2FsZV9jb2xvdXJfdmlyaWRpc19jKG9wdGlvbj0idmlyaWRpcyIsICkgKw0KICAgIHNjYWxlX3NpemVfY29udGludW91cyhyYW5nZSA9IGMoMSwgMTApLCBicmVha3MgPSBjKDAsIDEuMywgMTAsIDEwMCkpICsNCg0KICAgICMgR2VvbXMNCiAgICAjIyMgMy4yLjAgVXNlIGNvbG91ciBhbmQgc2l6ZSBpbiBvdXIgZG90cGxvdCB0byByZXByZXNlbnQgTDJGQyBhbmQgcGFkag0KICAgIGdlb21fcG9pbnQoYWVzKGNvbG91ciA9IC4uLiwgc2l6ZSA9IC4uLikpDQpgYGANCg0KLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQoNCk5vdyB3ZSBjYW4gbW9yZSBjbGVhcmx5IHNlZSB3aGljaCB2YWx1ZXMgYXJlIHdvcnRoIG5vdGluZyBvciBpbnZlc3RpZ2F0aW5nLiBXZSBjYW4gc2VlIHRoYXQgQ0NMMiBoYXMgYSBtb3JlIGNvbnNpc3RlbnQgYEwyRkNgIGFuZCBgcGFkamAgY29tYmluYXRpb24gYWx0aG91Z2ggdGhpcyB3b3VsZCByZXF1aXJlIGZ1cnRoZXIgaW52ZXN0aWdhdGlvbiBzaW5jZSBvdXIgc2l6ZSByYW5nZSBpcyBzdGlsbCBxdWl0ZSBsaW1pdGVkLiBXZSBjYW4gZnVydGhlciB6b29tIGluIG9uIG91ciBkYXRhIGJ5IHNwZWNpZmljYWxseSB2aXN1YWxpemluZyBvdXIgZ2VuZXMgb2YgaW50ZXJlc3QhDQoNCiMjIyAzLjIuMSBMb29raW5nIGF0IHNpbmdsZSBnZW5lcyB3aXRoIGRvdCBwbG90cw0KDQpXZSBjYW4gZnVydGhlciBuYXJyb3cgb3VyIHNlYXJjaCBpbnN0ZWFkIG9mIGxvb2tpbmcgYXQgKndob2xlIGdlbmUgZ3JvdXBzKiwgYW5kIHBpY2sgYSBzbWFsbCBzZXQgb2Ygc3BlY2lmaWMgZ2VuZXMuIFdlJ2xsIHVzZSBhIGRvdHBsb3QgdG8gdmlzdWFsaXplIHRoZSBkYXRhIHdoZXJlIHRoZSB5LWF4aXMgdXNlcyB0aGUgTDJGQyB2YWx1ZSB0byBwbGFjZSBwb2ludHMgYW5kIHdlJ2xsIHVzZSBhIGNhdGVnb3JpY2FsIHgtYXhpcyBvZiBnZW5lIGxpc3RzLiBXZSdsbCBmaWx0ZXIgb3VyIGRhdGEgdG8gb25seSBpbmNsdWRlIGluZmVjdGlvbnMgaW4gdGhlIEE1NDkgaG9zdCwgYW5kIGFsc28gZ3JvdXAgb3VyIGRhdGEgaW4gYSBmZXcgd2F5czoNCg0KMS4gIFdlJ2xsIHNjYWxlIG91ciBwb2ludCBzaXplcyBiYXNlZCBvbiB0aGUgYWRqdXN0ZWQgcC12YWx1ZSB0byBnZXQgYSBiZXR0ZXIgc2Vuc2Ugb2YgaG93IHByb2JhYmxlIHRoZXNlIGNoYW5nZXMgYXJlLg0KMi4gIFdlJ2xsIHVzZSBjb2xvdXIgdG8gZGVmaW5lIHRoZSBwYXRob2dlbiB1c2VkIGluIGVhY2ggZXhwZXJpbWVudC4NCjMuICBXZSdsbCBhbHNvIGZhY2V0IG91ciBkYXRhIGJhc2VkIG9uIGhvc3QuDQoNCmBgYHtyLCBmaWcud2lkdGg9MjAsIGZpZy5oZWlnaHQ9MTR9DQoNCnNpbmdsZV9nZW5lcyA9IGMoIkNDTDUiLCAiQ0NMMiIsICJDQ0wyMCIsICJDQ0wxNyIsICJJTDYiLCAiSUwxQSIpDQoNCmJsYW5jb19kYXRhX2xvbmcuZGYgJT4lIA0KICBmaWx0ZXIoR2VuZU5hbWUgJWluJSBzaW5nbGVfZ2VuZXMsDQogICAgICAgICkgJT4lIA0KICANCiAgIyAxLiBEYXRhDQogIGdncGxvdCguKSArDQogICAgIyAyLiBBZXN0aGV0aWNzDQogICAgIyMjIDMuMi4wIHNldCB0aGUgYWVzdGhldGljcyBmb3Igb3VyIGRvdCBwbG90IGNvbG91cmluZyBieSBwYXRob2dlbiwgc2hhcGluZyBieSBob3N0DQogICAgYWVzKHg9R2VuZU5hbWUsIHkgPSBMMkZDLCBjb2xvdXI9Li4uLCBzaGFwZT0uLi4pICsNCg0KICAgICMgVGhlbWUNCiAgICB0aGVtZV9idygpKw0KICAgIHRoZW1lKHRleHQgPSBlbGVtZW50X3RleHQoc2l6ZT0yMCkpICsNCg0KICAgICMgMy4gU2NhbGluZw0KICAgIHNjYWxlX2NvbG91cl92aXJpZGlzX2QoZGlyZWN0aW9uID0gLTEsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgZ3VpZGUgPSBndWlkZV9sZWdlbmQob3ZlcnJpZGUuYWVzID0gbGlzdChzaXplID0gNSkpKSArDQogICAgIyBTY2FsZSB0aGUgc2l6ZSByYW5nZQ0KICAgIHNjYWxlX3NpemUocmFuZ2UgPSBjKDgsIDQpKSArDQogICAgc2NhbGVfc2hhcGUoZ3VpZGUgPSBndWlkZV9sZWdlbmQob3ZlcnJpZGUuYWVzID0gbGlzdChzaXplID0gNSkpKSArDQoNCiAgICAjIDQuIEdlb21zDQogICAgIyMjIDMuMi4wIGFkanVzdCB0aGUgZG90IHNpemUgYmFzZWQgb24gdGhlIGFkanVzdGVkIHAtdmFsdWUNCiAgICBnZW9tX3BvaW50KGFlcyhzaXplID0gLi4uKSwgYWxwaGEgPSAwLjgsIHN0cm9rZSA9IDIpICsNCg0KICAgICMgNi4gRmFjZXRzDQogICAgZmFjZXRfd3JhcCh+IGhvc3QsIG5jb2wgPSAyLCBzY2FsZXMgPSAiZnJlZV95IikNCmBgYA0KDQotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCg0KIyMjIDMuMi4xIERvdCBwbG90IGludGVycHJldGF0aW9uDQoNCkZvY3VzaW5nIG9uIHNtYWxsIHNldHMgb2YgZ2VuZXMgd2UgY2FuIHNlZSBzb21lIHRyZW5kcyBhY3Jvc3Mgb3VyIGdyb3Vwcy4gRm9yIGluc3RhbmNlLCAzIG9mIG91ciBwYXRob2dlbnMgcHJvZHVjZSBhIHN0cm9uZyBJTDYgcmVzcG9uc2UgYXMgd2UndmUgc2VlbiBpbiBvdXIgaGVhdG1hcHMuIFdlIGFsc28gc2VlIGEgXD4yIEwyRkMgaW4gQ0NMMiBhcyBhIHJlc3BvbnNlIHRvIGluZmVjdGlvbiBieSBTQVJTLUNvVi0yLiBBY3R1YWxseSBvdXIgd2Vha2VzdCBJTDYgcmVzcG9uc2UgdG8gU0FSUy1Db1YtMiB3YXMgc2VlbiBpbiB0aGUgQUNFLTItZXhwcmVzc2luZyBBNDU5IGhvc3RzIHNvIG91ciBpbml0aWFsIGhlYXRtYXAgaW4gc2VjdGlvbiAzLjEuMCBkaWRuJ3QgZ2l2ZSB1cyBhIGZ1bGwgcGljdHVyZSBvZiB3aGF0IG1pZ2h0IGJlIGhhcHBlbmluZy4NCg0KSXQgd291bGQgYmUgYmVzdCB0byBmdXJ0aGVyIGludmVzdGlnYXRlIHRoZXNlIG9uIGEgbW9yZSBzcGVjaWZpYyBzdWJzZXQgb2YgaG9zdHMgYW5kL29yIHBhdGhvZ2Vucy4NCg0KOjo6IHsuYWxlcnQgLmFsZXJ0LWJsb2NrIC5hbGVydC1kYW5nZXJ9DQoqKkNvbXByZWhlbnNpb24gUXVlc3Rpb24gMy4wLjAqKiBXaGlsZSB3ZSBzZWUgc29tZSBpbmNvbnNpc3RlbmN5IGluIG91ciBJTDYvU0FSUy1Db1YtMiByZXNwb25zZSBpbiB0aGUgYWJvdmUgZG90cGxvdCwgYXJlIHdlIHBvc3NpYmx5IG1pc3Npbmcgc29tZSBjb250ZXh0PyBJZiB5b3Ugd2VyZSB0byByZXR1cm4gdG8gdGhlIHNvdXJjZSBvZiB0aGlzIGRhdGEgcHJpb3IgdG8gb3VyIHdyYW5nbGluZyAoKipzZWUgc2VjdGlvbiAxLjUuMioqKSwgeW91IG1pZ2h0IG5vdGUgdGhhdCB0aGVyZSBhcmUgMyBraW5kcyBvZiBBNTQ5LUFDRTIgZXhwZXJpbWVudGFsIGdyb3Vwcy4gV2hhdCBhcmUgc29tZSB3YXlzIHlvdSBjb3VsZCBhY2NvdW50IGZvciB0aGlzIGluZm9ybWF0aW9uIHdoZW4gd3JhbmdsaW5nPw0KOjo6DQoNCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KDQojIDQuMC4wIFJOQXNlcSBhbmFseXNpcyBmb3Igc2lnbmlmaWNhbnQgZnVuY3Rpb25hbCB0ZXJtcw0KDQpVcCB1bnRpbCB0aGlzIHBvaW50LCB3ZSd2ZSBiZWVuIGZlZWxpbmcgb3VyIHdheSB0aHJvdWdoIHRoZSBkYXRhLCByYXRoZXIuLi4gKnVuZGlyZWN0ZWQqLiBPZnRlbiB3aXRoaW4gbWFudXNjcmlwdHMgeW91IHdpbGwgZmluZCBkZWVwZXIgYW5hbHlzZXMgb2YgZGlmZmVyZW50aWFsIGV4cHJlc3Npb24gYnkgZXhhbWluaW5nIHRoZSBjb2xsZWN0aW9uIG9mIGZ1bmN0aW9uYWwgdGVybXMgdG8gZGV0ZXJtaW5lIHRyZW5kcyBvciBwYXRod2F5cyBvZiBpbnRlcmVzdC4gVGhlIEdlbmUgT250b2xvZ3kgKEdPKSByZXNvdXJjZSByZXByZXNlbnRzIGEgY29kaWZpY2F0aW9uIG9mIGdlbmUgZnVuY3Rpb24gdGhyb3VnaCB0aHJlZSBkb21haW5zOiBjZWxsdWxhciBjb21wb25lbnQgKENDKSwgbW9sZWN1bGFyIGZ1bmN0aW9uIChNRiksIGFuZCBiaW9sb2dpY2FsIHByb2Nlc3MgKEJQKS4NCg0KTW9zdCwgaWYgbm90IGFsbCwgb2YgdGhlIGdlbmVzIGluIG91ciBkYXRhc2V0IGNhbiBiZSBkZXNjcmliZWQgaW4gc29tZSB3YXkgYnkgYSBHTyB0ZXJtLiBPbmNlIHdlIGNvbGxlY3QgdGhlc2UgdGVybXMsIHdlIGNhbiBiZWdpbiB0byBsb29rIGZvciBtZWFuaW5nZnVsIGdyb3VwcyB0aGF0IGNvdWxkIGJlIGNvbnNpZGVyZWQgb3Zlci1yZXByZXNlbnRlZCAob3IgdW5kZXItcmVwcmVzZW50ZWQpIGluIG91ciBkYXRhc2V0LiBPbmNlIHdlIGhhdmUgY29sbGVjdGVkIHRoaXMgaW5mb3JtYXRpb24gd2UgY2FuIHZpc3VhbGl6ZSBpdCBzaW1pbGFyIHRvIG91ciBpbmRpdmlkdWFsIGdlbmUgZG90cGxvdHMuDQoNCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KDQojIyA0LjEuMCBVc2UgdGhlIGBnb3NlcWAgcGFja2FnZSB0byBwdWxsIGRvd24gR08gdGVybXMgZm9yIHlvdXIgZ2VuZXMNCg0KVXNpbmcgdGhlIGBnb3NlcWAgcGFja2FnZSB3ZSBjYW4gcGVyZm9ybSBhIGZ1bGwgR08gdGVybSBhbmFseXNpcyBvbiBvdXIgREUgZGF0YSB3aXRoIGFuIGFuYWx5c2lzIGZvciBlbnJpY2hlZCB0ZXJtcyB3aXRoaW4gb3VyIHNldCBvZiBvdmVyLSBvciB1bmRlci1leHByZXNzZWQgZ2VuZXMuIEhvd2V2ZXIsIGluIG9yZGVyIHRvIGJlZ2luIHRoZSBwcm9jZXNzIG9mIHB1bGxpbmcgZG93biBHTyB0ZXJtcywgd2l0aCB0aGUgYGdvc2VxYCBwYWNrYWdlLCB3ZSBhbHNvIG5lZWQgdG8gZ2VuZXJhdGUgYSBuYW1lZCBpbnRlZ2VyIHZlY3RvciB0byBzcGxpdCBvdXIgZ2VuZSBzZXRzIGludG8gdGhlIHR3byByZWxldmFudCBjYXRlZ29yaWVzLg0KDQpXZSdsbCBzdG9yZSBvdXIgY2F0ZWdvcml6ZWQgc2V0IG9mIGdlbmVzIGluIGBnZW5lc19TQVJTX0NvVl8yYCB3aGVyZSBhIDEgcmVwcmVzZW50cyBvdmVyL3VuZGVyZXhwcmVzc2VkIGdlbmVzIGluIG91ciBzZXQsIGFuZCAwIHJlcHJlc2VudHMgYWxsIHJlbWFpbmluZyBnZW5lcy4NCg0KYGBge3J9DQojIFNldCB1cCB0aGUgZ2VuZSBpbmZvcm1hdGlvbiB3ZSBuZWVkIGZvciB0aGUgR08tdGVybSBhbmFseXNpcw0KIyBJdCBuZWVkcyB0byBrbm93IHdoaWNoIGdlbmVzIG1lZXQgb3VyIEZDIGNyaXRlcmlhIGFuZCB3aGljaCBkbyBub3QNCiNucm93KGJsYW5jb19kYXRhLmRmKQ0KDQojIENhc3Rpbmcgb3VyIGxvZ2ljYWwgYXMgYW4gaW50ZWdlciB3aWxsIGNyZWF0ZSBhIDAvMSBtYXRyaXgNCmdlbmVzX1NBUlNfQ29WXzIgPC0gYXMuaW50ZWdlcihibGFuY29fZGF0YS5kZiRgcGFkal9TQVJTLUNvVi0yKEE1NDkpYCA8IDAuMDUgJiAjIGxvdyBwLXZhbHVlDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYWJzKGJsYW5jb19kYXRhLmRmJGBTQVJTLUNvVi0yKEE1NDkpX0wyRkNgKSA+IDEuNSkgIyBhYnNvbHV0ZSBMMkZDID4gMS41DQoNCiMgV2UnbGwgbmFtZSBlYWNoIGVsZW1lbnQgaW4gb3VyIHZlY3RvciBieSB0aGUgZ2VuZSBuYW1lcw0KbmFtZXMoLi4uKSA8LSBibGFuY29fZGF0YS5kZiRHZW5lTmFtZQ0KDQojIFdoYXQgaXMgb3VyIG9iamVjdD8NCnN0cihnZW5lc19TQVJTX0NvVl8yKQ0KDQojIFN1bW1hcml6ZSBvdXIgdmVjdG9yDQp0YWJsZShnZW5lc19TQVJTX0NvVl8yKQ0KYGBgDQoNCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KDQpTbyBhIHF1aWNrIGJpdCBvZiBmaWx0ZXJpbmcgYW5kIHdlIGNhbiBzZWUgZnJvbSBvdXIgdGFibGUgc3VtbWFyeSwgdGhhdCB3ZSBoYXZlIGNsb3NlIHRvIDUwMCBnZW5lcyB0aGF0IGFyZSBzaWduaWZpY2FudGx5IGRpZmZlcmVudGlhbGx5IGV4cHJlc3NlZCBhYm92ZSBvciBiZWxvdyAxLjUgTDJGQy4gTm93IHdlIGhhdmUgdG8gZmlndXJlIG91dCBob3cgdG8gbWFwIHRoZXNlIHRvIEdPIHRlcm1zIQ0KDQojIyMgNC4xLjEgQ2hvb3NlIHRoZSBjb3JyZWN0IGRhdGFiYXNlIHRvIG1hcCB5b3VyIGdlbmUgc3ltYm9scyB0byBHTyB0ZXJtcw0KDQpUaGUgYGdvc2VxYCBwYWNrYWdlIGlzIGNvbXBhdGlibGUgd2l0aCBhIG51bWJlciBvZiBvcmdhbmlzbXMgYW5kIGdlbmUgbmFtZXMgZm9yIG1hcHBpbmcgeW91ciBkYXRhIHRvIHRoZSBHTyBkYXRhYmFzZS4gVG8gdmlldyAqd2hpY2gqIG9yZ2FuaXNtcyBhcmUgc3VwcG9ydGVkLCB3ZSBjYW4gdXNlIGBzdXBwb3J0ZWRPcmdhbmlzbXMoKWAgdG8gdmlldy4gU2luY2UgdGhlIHJhdyBSTkEtU2VxIHJlYWRzIHdlcmUgYWxpZ25lZCB0byB0aGUgaHVtYW4gZ2Vub21lIHVzaW5nIHRoZSBoZzE5IGFzc2VtYmx5IChzZWUgQmxhbmNvLU1lbG8gZXQgYWwuLCAyMDIwKSwgd2UnbGwgYmUgd29ya2luZyB3aXRoIGBoZzE5YCBhcyB3ZWxsLiBXZSdsbCB1c2UgdGhlIGFubm90YXRpb24gaW5mb3JtYXRpb24gcHJvdmlkZWQgZm9yIHRoZSBuZXh0IHBhcnQgb2Ygb3VyIGFuYWx5c2lzLg0KDQpGaXJzdCwgaG93ZXZlciwgbGV0J3MgY2hlY2sgdGhhdCBoZzE5IGlzIGluZGVlZCBhIHN1cHBvcnRlZCBnZW5vbWUgYW5ub3RhdGlvbi4NCg0KYGBge3J9DQojIFB1bGwgZG93biB0aGUgc3VwcG9ydGVkIG9yZ2FuaXNtcyBhbmQgbG9vayBmb3IgdGhlIGh1bWFuICJoZzE5IiBnZW5vbWUNCnN1cHBvcnRlZE9yZ2FuaXNtcygpICU+JSANCmZpbHRlciguLi4pDQpgYGANCg0KLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQoNCiMjIyA0LjEuMiBGaXR0aW5nIGEgcHJvYmFiaWxpdHkgd2VpZ2h0aW5nIGZ1bmN0aW9uIChQV0YpIHRvIG91ciBkYXRhc2V0DQoNCkxvb2tpbmcgYXQgb3VyIHRhYmxlIGFib3ZlIHdlIGNhbiBpbW1lZGlhdGVseSBzZWUgdGhhdCB0aGVyZSBhcmUgMyBwb3RlbnRpYWwgYW5ub3RhdGlvbnMgc2V0cyB0byBkcmF3IGZyb20uIEVhY2ggdXNlcyBhIGRpZmZlcmVudCBraW5kIG9mIElEIGRlc2NyaXB0aW9uOiBFbnRyZXogR2VuZSBJRCwgRW5zZW1ibGUgZ2VuZSBJRCwgYW5kIEdlbmUgU3ltYm9sLiBXaGljaCBvbmUgZG8gd2UgdXNlPyBUaGlzIGRlcGVuZHMgb24gaG93IG91ciBkYXRhIGlzIGZvcm1hdHRlZCBidXQgbGV0J3MgdGFrZSBhIHF1aWNrIGxvb2sgYXQgdGhpcyBzY3JlZW5zaG90IG9mIENDTDggZW50cnkgZnJvbSBOQ0JJOg0KDQo6Ojoge2FsaWduPSJjZW50ZXIifQ0KPGltZyBzcmM9Imh0dHBzOi8vZ2l0aHViLmNvbS9jYW1vay9DU0JfQ291cnNlX01hdGVyaWFscy9ibG9iL21haW4vQWR2Vml6L0dlbmVJbmZvcm1hdGlvbi5wbmc/cmF3PXRydWUiIHdpZHRoPSI4MDAiLz4NCg0KSXQncyBpbXBvcnRhbnQgdG8ga25vdyB0aGUgZGlmZmVyZW5jZSBiZXR3ZWVuIHlvdXIgZ2VuZSBzeW1ib2xzIGFuZCB2YXJpb3VzIGlkZW50aWZpZXJzIQ0KOjo6DQoNCktub3dpbmcgd2hhdCB3ZSd2ZSBzZWVuIG9mIHRoZSBkYXRhIGFscmVhZHksIHRoZSBpbmZvcm1hdGlvbiB3ZSdyZSBsb29raW5nIGZvciBpcyBpbiB0aGUgdGhpcmQgcm93LiBVc2luZyB0aGUgZ2VuZSBzeW1ib2xzIGluIG91ciBkYXRhLCB3ZSBjYW4NCg0KMS4gIE1hcCBvdXIgZ2VuZSBzeW1ib2xzIHRvIEdPIGFubm90YXRpb25zLCBhbmQNCjIuICBHYXRoZXIgdGhlIGdlbmUgbGVuZ3RocyBmb3Igb3VyIGRhdGEuDQoNClRoZSBzZWNvbmQgcG9ydGlvbiBvZiBpbmZvcm1hdGlvbiBpcyByZXF1aXJlZCBpbiB0aGUgZ2VuZXJhdGlvbiBvZiBhIHByb2JhYmlsaXR5IHdlaWdodGluZyBmdW5jdGlvbiAoUFdGKS4gVGhpcyBQV0Ygd2lsbCBkZXRlcm1pbmUgYSB3ZWlnaHRpbmcgZm9yIGVhY2ggZ2VuZSB3aGljaCBpcyBlc3NlbnRpYWxseSB0aGUgcHJvYmFiaWxpdHkgb2YgYSBnZW5lIGJlaW5nIGRpZmZlcmVudGlhbGx5IGV4cHJlc3NlZCBiYXNlZCBvbiBpdHMgbGVuZ3RoIGFsb25lLiBJdCdzIGltcG9ydGFudCB0byBnZW5lcmF0ZSB3aXRoIGluZm9ybWF0aW9uIGFzIGl0IGNhbiBpbmZsdWVuY2Ugb3VyIGVucmljaG1lbnQgYW5hbHlzaXMuIFVsdGltYXRlbHkgd2UgdXNlIHRoZSBQV0YgdG8gaGVscCBpbmZvcm0gdGhlIGNyZWF0aW9uIG9mIGEgbnVsbCBkaXN0cmlidXRpb24gaW4gb3VyIGFuYWx5c2lzLg0KDQpXZSdsbCB1c2UgdGhlIGBudWxscCgpYCBmdW5jdGlvbiB0byBnZW5lcmF0ZSB0aGUgUFdGIGZvciBvdXIgZGF0YXNldC4NCg0KYGBge3J9DQojIEdlbmVyYXRlIHRoZSBQV0YgZm9yIG91ciB2ZWN0b3Igb2YgZ2VuZXMNCnB3Zl9TQVJTX0NvVl8yIDwtIG51bGxwKERFZ2VuZXMgPSAuLi4sICAgICAgIyBPdXIgbGlzdCBvZiBERSBnZW5lcw0KICAgICAgICAgICAgICAgICAgICAgICAgZ2Vub21lID0gLi4uLCAgICAgICAgICAgICAgICAgIyBUaGUgR08gYW5ub3RhdGlvbiB3ZSB3YW50IHRvIHVzZQ0KICAgICAgICAgICAgICAgICAgICAgICAgaWQgPSAuLi4pICAgICAgICAgICAgICAgIyBUaGUgaWQgd2UnbGwgdXNlIHRvIGdlbmVyYXRlIHRoZSBkYXRhIHVzaW5nIGdlbmUgc3ltYm9scw0KDQpoZWFkKHB3Zl9TQVJTX0NvVl8yKQ0KYGBgDQoNCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KDQo6Ojogey5hbGVydCAuYWxlcnQtYmxvY2sgLmFsZXJ0LXdhcm5pbmd9DQoqKkxvb2tpbmcgYXQgYSBwbG90IG9mIG91ciBQV0Y6KiogaGVyZSB3ZSBzZWUgYSBzdWJzZXQgb2YgZ2VuZXMgYW5kIHRoZWlyIFBXRiB2YWx1ZXMgdG8gc2VlIGhvdyB0aGV5IGZpdCBhY2NvcmRpbmcgdG8gb3VyIGxpbmUgb2YgYmVzdCBmaXQuIFRoZSBnb2FsIGlzIGZvciB0aGUgcG9pbnRzIHRvIGJlIHJlbGF0aXZlbHkgY2xvc2UgdG8gb3VyIGxpbmUgb2YgZml0LiBJZiB0aGUgdmFsdWVzIGFyZSBmYXIgb2ZmLCB0aGVyZSBtYXkgYmUgYW4gaXNzdWUgd2l0aCB0aGUgYW5ub3RhdGlvbiBkYXRhIG9yIGhvdyB5b3UgY2FsY3VsYXRlZCB5b3VyIGdlbmUgbGVuZ3RocyBmb3IgdGhlIFBXRi4NCjo6Og0KDQotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCg0KIyMjIDQuMS4zIEdlbmVyYXRlIGVucmljaGVkIEdPIHRlcm1zIGZyb20gb3VyIERFIGRhdGENCg0KV2UncmUgbm93IHJlYWR5IHRvIHF1ZXJ5IHRoZSBHTyBkYXRhYmFzZSBmb3IgdXAtIGFuZCBkb3duLXJlZ3VsYXRlZCB0ZXJtcyBpbiBvdXIgZGF0YS4gV2UnbGwgdXNlIHRoZSBgZ29zZXEoKWAgY29tbWFuZCB0byBkbyB0aGlzLCBwcm92aWRpbmcgb3VyIFBXRiBvYmplY3QgYXMgd2VsbCBhcyB0aGUgY29ycmVjdCBkYXRhYmFzZSBpbmZvcm1hdGlvbi4gVGhlIGRlZmF1bHQgbWV0aG9kIHVzZWQgaW4gdGhpcyBwcm9jZXNzIHRvIGdlbmVyYXRlIG91ciBlbnJpY2htZW50IGFuYWx5c2lzIGlzIHRoZSAqKipXYWxsZW5pdXMgYXBwcm94aW1hdGlvbioqKi4gWW91IGNhbiBjaG9vc2UgdG8gdXNlIHJhbmRvbSBzYW1wbGluZyBidXQgdGhpcyBtZXRob2QgY2FuIHRha2UgbXVjaCBsb25nZXIgd2l0aCBsaXR0bGUgZGlmZmVyZW5jZSBpbiByZXN1bHRzLg0KDQpGb3IgbW9yZSBpbmZvcm1hdGlvbiBvbiB0aGlzIHByb2Nlc3Mgc2VlIHRoZSBgZ29zZXFgIHBhY2thZ2UgdmlnbmV0dGUgW2hlcmVdKGh0dHBzOi8vYmlvY29uZHVjdG9yLm9yZy9wYWNrYWdlcy9kZXZlbC9iaW9jL3ZpZ25ldHRlcy9nb3NlcS9pbnN0L2RvYy9nb3NlcS5wZGYpDQoNCmBgYHtyfQ0KIyBHZW5lcmF0ZSBvdXIgZ28gZW5yaWNobWVudCBhbmFseXNpcw0KR08ud2FsbF9TQVJTX0NvVl8yIDwtIGdvc2VxKHB3ZiA9IC4uLiwgICAgICAjIFByb3ZpZGUgb3VyIHB3ZiBvYmplY3QNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBnZW5vbWUgPSAiaGcxOSIsICAgICAgICAgICAjIFNldCB0aGUgYW5ub3RhdGlvbiBzZXQgdG8gdXNlDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgaWQgPSAiZ2VuZVN5bWJvbCIsICAgICAgICAgIyBXaGljaCB2YXJpYWJsZSB3aWxsIHdlIHVzZSB0byBjb21wYXJlIHdpdGgNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBtZXRob2QgPSAuLi4gICAgICAgIyBIb3cgd2lsbCB5b3UgZGVjaWRlIG9uIHRoZSBudWxsIGRpc3RyaWJ1dGlvbg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgKQ0KDQojIFdoYXQgZG9lcyB0aGUgZmluYWwgcmVzdWx0IGxvb2sgbGlrZT8NCmhlYWQoR08ud2FsbF9TQVJTX0NvVl8yKQ0KYGBgDQoNCmBgYHtyfQ0KIyBXaGF0IGlzIHRoZSBzdHJ1Y3R1cmUgb2Ygb3VyIHJlc3VsdGluZyBkYXRhPw0Kc3RyKEdPLndhbGxfU0FSU19Db1ZfMikNCmBgYA0KDQotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCg0KIyMgNC4yLjAgSW50ZXJwcmV0aW5nIG91ciBgZ29zZXFgIHJlc3VsdHMNCg0KTG9va2luZyBhdCB0aGUgcmVzdWx0cyBvZiBvdXIgYW5hbHlzaXMsIHdlIGFyZSByZXR1cm5lZCBhIGxpc3Rpbmcgb2YgMjIsNTAwIHVuaXF1ZSBHTyB0ZXJtcyBhY3Jvc3MgdGhlIDMgZG9tYWlucyAoQ0MsIEJQLCBhbmQgTUYpLiBXZSBhcmUgZ2l2ZW4gdHdvIHNldHMgb2YgcC12YWx1ZXMsIG9uZSBlYWNoIGZvciBvdmVyLXJlcHJlc2VudGVkIGFuZCB1bmRlci1yZXByZXNlbnRlZCB0ZXJtcy4gV2l0aCBlYWNoIHRlcm0gd2UgYWxzbyBzZWUgaG93IG1hbnkgdGltZXMgdGhhdCB0ZXJtIGlzIHJlcHJlc2VudGVkIGluIG91ciBERSBkYXRhIHZzIHRoZSB0b3RhbCBudW1iZXIgb2YgdGltZXMgdGhhdCB0ZXJtIG1heSBhcHBlYXIgaW4gdGhlIEdPIGRhdGFiYXNlIHdlIHF1ZXJpZWQuIEFzIHlvdSBjYW4gc2VlIGZyb20gdGhlIGZpcnN0IGZldyByb3dzIG9mIGRhdGEsIHNvbWUgR08gdGVybSBjYXRlZ29yaWVzIGNhbiBlbmNvbXBhc3MgYSBsYXJnZSBudW1iZXIgb2YgZ2VuZXMuDQoNCjo6OiB7LmFsZXJ0IC5hbGVydC1ibG9jayAuYWxlcnQtd2FybmluZ30NCioqUmVtZW1iZXIgaG93IHdlIGNyZWF0ZWQgdGhpcyBkYXRhISoqIExldCdzIGFsc28gcmVtaW5kIG91cnNlbHZlcyB0aGF0IHdlIHNldCB1cCBvdXIgREUgZ2VuZSBsaXN0IHVzaW5nIHRoZSBkYXRhIGZyb20gQTQ1OSBjZWxscyBpbmZlY3RlZCB3aXRoIFNBUlMtQ29WLTIuIExvb2tpbmcgYXQgdGhlIG1hbnVzY3JpcHQgaXRzZWxmLCB3aGlsZSBoYXZpbmcgYSBkaXN0aW5jdCBpbW11bmUgcmVzcG9uc2UsIHRoZSByZXBsaWNhdGlvbiBvZiB0aGUgdmlydXMgd2FzIHZlcnkgbG93IGluIHRoZXNlIGNlbGwgdHlwZXMgd2hpY2ggbGFjayB0aGUgZXhwcmVzc2lvbiBvZiB0aGUgQUNFMiByZWNlcHRvci4gV2UncmUgcmVhbGx5IGp1c3QgdXNpbmcgdGhpcyBkYXRhIHRvIG1ha2Ugc29tZSB2aXN1YWxpemF0aW9ucyENCjo6Og0KDQpUbyBiZWdpbiBvdXIgYW5hbHlzaXMgd2UgY2FuIGZpbHRlciB0aGUgZW5yaWNoZWQgR08gdGVybXMgc2V0cyBieSBhcHBseWluZyBhIG11bHRpcGxlIGh5cG90aGVzaXMgdGVzdCBjb3JyZWN0aW9uIHdpdGggYHAuYWRqdXN0KClgIHVzaW5nIHRoZSBbQmVuamFtaW5pLUhvY2hiZXJnIG1ldGhvZCB0byBtaW5pbWl6ZSB0aGUgZmFsc2UgZGlzY292ZXJ5IHJhdGVdKGh0dHBzOi8vd3d3LnB1YmxpY2hlYWx0aC5jb2x1bWJpYS5lZHUvcmVzZWFyY2gvcG9wdWxhdGlvbi1oZWFsdGgtbWV0aG9kcy9mYWxzZS1kaXNjb3ZlcnktcmF0ZSkuDQoNCmBgYHtyfQ0KIyBHZW5lcmF0ZSBhbiBlbnJpY2hlZCBHbyB0ZXJtIGRhdGFzZXQNCmVucmljaGVkLkdPX1NBUlNfQ29WXzIgPC0gDQojIEFkanVzdCB0aGUgcC12YWx1ZXMgb2YgdGhlIG92ZXItcmVwcmVzZW50ZWQgY29sdW1uDQojIEdPLndhbGxfU0FSU19Db1ZfMltwLmFkanVzdChHTy53YWxsX1NBUlNfQ29WXzIkb3Zlcl9yZXByZXNlbnRlZF9wdmFsdWUsIG1ldGhvZD0iQkgiKSA8IDAuMDUsIF0NCg0KR08ud2FsbF9TQVJTX0NvVl8yICU+JSANCmZpbHRlciguLi4pDQoNCiMgSG93IG1hbnkgdGVybXMgY29tZSBiYWNrIGZyb20gb3VyIGZpbHRlcmluZz8NCnN0cihlbnJpY2hlZC5HT19TQVJTX0NvVl8yKQ0KYGBgDQoNCmBgYHtyfQ0KIyBMb2FkIG91ciBnb3NlcSBkYXRhIHRvIGxvb2sgYXQgYW5kIHZpc3VhbGl6ZQ0KIyBUaGlzIHdpbGwgYWxzbyBjb250YWluIHNvbWUgYWRkaXRpb25hbCBkYXRhc2V0cyB3ZSdsbCB3YW50IHRvIGxvb2sgYXQuDQpsb2FkKCIuL2RhdGEvTGVjdHVyZTA0LlJEYXRhIikNCmBgYA0KDQpgYGB7cn0NCiMgV2hhdCBraW5kIG9mIHRlcm1zIGRvIHdlIHNlZSBpbiBvdXIgZW5yaWNobWVudD8NCmVucmljaGVkLkdPX1NBUlNfQ29WXzIkLi4uDQpgYGANCg0KLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQoNCiMjIyA0LjIuMSBQbG90IG91ciBlbnJpY2hlZCBHTyB0ZXJtIGNhdGVnb3JpZXMgYXMgYSBkb3QgcGxvdA0KDQpMb29raW5nIGF0IG91ciBlbnJpY2hlZCBzZXQgb2YgR08gdGVybXMsIHdlIHNlZSB0ZXJtcyBsaWtlIGBpbmZsYW1tYXRvcnkgcmVzcG9uc2VgIGFuZCBgcmVzcG9uc2UgdG8gc3RyZXNzYC4gTm93IHRoYXQgd2UgaGF2ZSBhIHNldCBvZiBlbnJpY2hlZCBHTyB0ZXJtcywgd2UgY2FuIGZpbHRlciBhbmQgcGxvdCB0aGlzIGluZm9ybWF0aW9uIGFzIGEgZG90cGxvdCB3aXRoIHRoZSBmb2xsb3dpbmcgY2hhcmFjdGVyaXN0aWNzOg0KDQoxLiAgR08gdGVybXMgbGlzdGVkIGFsb25nIHRoZSB5LWF4aXMNCjIuICBzaXplIG9mIGRvdHMgcmVwcmVzZW50aW5nIGNhdGVnb3J5IHNpemUNCjMuICB1c2UgY29sb3VyIHRvIHJlcHJlc2VudCB0aGUgJSBvZiBERSBnZW5lcyBwcmVzZW50IGZyb20gdGhhdCB0aGF0IGNhdGVnb3J5DQoNCkxldCdzIGxvb2sgYXQgdGVybXMgdGhhdCBpbmNsdWRlIHRoZSB3b3JkICJyZXNwb25zZSIuDQoNCmBgYHtyLCBmaWcud2lkdGg9MTIsIGZpZy5oZWlnaHQ9MTB9DQoNCmVucmljaGVkLkdPX1NBUlNfQ29WXzIgJT4lIA0KICAjIEdlbmVyYXRlIGEgcGVyY2VudGFnZSB2YWx1ZSANCiAgbXV0YXRlKHBlcmNlbnRPZkNhdCA9IG51bURFSW5DYXQvbnVtSW5DYXQsDQogICAgICAgICAjIENyZWF0ZSBhIGR1bW15IGNhdGVnb3J5DQogICMgICAgICAgIHBhdGhvZ2VuID0gIlNBUlMtQ29WLTIiLA0KICAgICAgICAgIyBNYWtlIGEgc2V0IG9mIGFkanVzdGVkIHAtdmFsdWVzDQogICAgICAgICBGRFIgPSBwLmFkanVzdChvdmVyX3JlcHJlc2VudGVkX3B2YWx1ZSwgbWV0aG9kID0gIkJIIikNCiAgICAgICkgJT4lIA0KICANCiAgIyBGaWx0ZXIgZm9yIG9ubHkgdGVybXMgdGhhdCBpbmNsdWRlIHRoZSB3b3JkICJyZXNwb25zZSINCiAgZmlsdGVyKHN0cl9kZXRlY3QodGVybSwgInJlc3BvbnNlIikpICU+JSANCiAgDQogICMgMS4gRGF0YQ0KICBnZ3Bsb3QoLikgKw0KICAgICMgMi4gQWVzdGhldGljcw0KICAgICMjIyA0LjIuMSBzZXQgYWVzdGhldGljcyBieSBudW1ERUluQ2F0IGFuZCBGRFINCiAgICBhZXMoeD0iU0FSUy1Db1YtMiIsIHk9Li4uLCBzaXplPS4uLiwgY29sb3VyID0gLi4uKSArDQogIA0KICAgICMgVGhlbWUNCiAgICB0aGVtZV9idygpICsNCiAgICB0aGVtZSh0ZXh0ID0gZWxlbWVudF90ZXh0KHNpemU9MjApKSArDQogIA0KICAgICMgMy4gU2NhbGluZw0KICAgIHNjYWxlX3NpemUocmFuZ2U9YygzLDEwKSkgKw0KICAgIHNjYWxlX2NvbG91cl92aXJpZGlzX2MoZGlyZWN0aW9uID0gLTEpICsgDQogIA0KICAgICMgNC4gR2VvbXMNCiAgICBnZW9tX3BvaW50KCkNCmBgYA0KDQotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCg0KIyMjIDQuMi4yIFBsb3QgbXVsdGlwbGUgY29uZGl0aW9ucyB0byB0aGUgc2FtZSBkb3QgcGxvdA0KDQpXaXRoIGEgbGl0dGxlIGJpdCBvZiBlbGJvdyBncmVhc2UsIHdlIGNhbiBnZW5lcmF0ZSBhIGRhdGEgZnJhbWUgd2l0aCAqbXVsdGlwbGUqIEdPIGVucmljaG1lbnQgYW5hbHlzZXMgZm9yIG11bHRpcGxlIGV4cGVyaW1lbnRzIGFuZCBub3cgd2UgY2FuIGNvbXBhcmUgZW5yaWNobWVudCBhY3Jvc3MgZGF0YSBzZXRzISBPdXIgYExlY3R1cmUwNC5SRGF0YWAgY29udGFpbmVkIHRoZSBjb21iaW5lZCBleHBlcmltZW50YWwgR08gcmVzdWx0cyBpbiB0aGUgb2JqZWN0IGBlbnJpY2hlZC5HT19jb21iaW5lZC5kZmAuDQoNCmBgYHtyfQ0KIyBUYWtlIGEgbG9vayBhdCBvdXIgY29tYmluZWQgZGF0YSBmcmFtZQ0KZW5yaWNoZWQuR09fY29tYmluZWQuZGYNCmBgYA0KDQpgYGB7ciwgZmlnLndpZHRoPTEyLCBmaWcuaGVpZ2h0PTEwfQ0KIyBMb2FkIHVwIGEgY29tYmluZWQgZW5yaWNobWVudCBhbmFseXNpcw0KIyBsb2FkKCIuL2RhdGEvTGVjdHVyZTA0LlJEYXRhIikNCg0KZW5yaWNoZWQuR09fY29tYmluZWQuZGYgJT4lIA0KDQogICMgMS4gRGF0YQ0KICBnZ3Bsb3QoLikgKw0KICAgICMgMi4gQWVzdGhldGljcw0KICAgICMjIyA0LjIuNCBTZXQgdGhlIG5ldyB4LWF4aXMgdmFsdWUgYW5kIGNvbG91ciBhZXN0aGV0aWMNCiAgICBhZXMoeD0uLi4sIHk9dGVybSwgc2l6ZT1udW1ERUluQ2F0LCBjb2xvdXIgPSAuLi4pICsNCiAgDQogICAgIyBUaGVtZQ0KICAgIHRoZW1lX2J3KCkgKw0KICAgIHRoZW1lKHRleHQgPSBlbGVtZW50X3RleHQoc2l6ZT0yMCkpICsNCiAgDQogICAgIyAzLiBTY2FsaW5nDQogICAgc2NhbGVfc2l6ZShyYW5nZT1jKDMsMTApKSArDQogICAgc2NhbGVfY29sb3VyX3ZpcmlkaXNfYyhkaXJlY3Rpb24gPSAtMSkgKyANCiAgDQogICAgIyA0LiBHZW9tcw0KICAgIGdlb21fcG9pbnQoKQ0KYGBgDQoNCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KDQojIyA0LjMuMCBVcHNldCBwbG90cw0KDQojIyMgNC4zLjEgQ2FuIFVwc2V0IHBsb3RzIGhlbHAgdXMgdG8gbWFrZSBzZW5zZSBvZiBvdmVybGFwcGluZyByZWxhdGlvbnNoaXBzPw0KDQpVcHNldCBwbG90cyBhcmUgYW4gYWx0ZXJuYXRpdmUgdG8gVmVubiBkaWFncmFtcyB0aGF0IHNob3cgdGhlIGludGVyc2VjdGlvbiBvZiBzZXRzLCBhcyB3ZWxsIGFzIHRoZSBzaXplIG9mIHRoZSBzZXRzLiBBZGRpdGlvbmFsbHksIFZlbm4gZGlhZ3JhbXMgY2FuIGJlIGRpZmZpY3VsdCB0byBpbnRlcnByZXQgZm9yIGdyZWF0ZXIgdGhhbiAyIG9yIDMgc2V0cy4gVGhpcyBpcyBhIHJlYWwgbGlmZSBmaWd1cmUgZnJvbSBCTUMgQmlvaW5mb3JtYXRpY3MuIFN1cmUgaXQgbG9va3MgcHJldHR5LCBidXQgd2hhdCBkb2VzIHRoZSBudW1iZXIgMjQgcmVwcmVzZW50IGluIHRoaXMgcGljdHVyZSBpbiB0ZXJtcyBvZiBBLCBCLCBDLCBELCBhbmQgRT8NCg0KOjo6IHthbGlnbj0iY2VudGVyIn0NCjxpbWcgc3JjPSJodHRwczovL2dpdGh1Yi5jb20vY2Ftb2svQ1NCX0NvdXJzZV9NYXRlcmlhbHMvYmxvYi9tYWluL0FkdlZpei9GaWcyX0xhbV9CTUNCaW9pbmZvcm1hdGljczIwMTZfMTcucG5nP3Jhdz10cnVlIiB3aWR0aD0iNTAwIi8+DQoNCldoYXQgaXMgdGhlIG1lYW5pbmcgb2YgdGhlIHZhbHVlIDI0IGluIHRoaXMgZGlhZ3JhbT8gU3RhcmUgYXQgaXQgbG9uZyBlbm91Z2ggYW5kIHlvdSdsbCBzZWUgd2hpY2ggZ3JvdXAgaXQncyBpbiBidXQgaW1hZ2luZSB0aGlzIHdhcyAxMCBncm91cHM/DQo6OjoNCg0KVGhlICoqVXBTZXQgcGxvdCoqIHdhcyBmaXJzdCBwdWJsaXNoZWQgaW4gMjAxNCBhbmQgaGFzIGJlY29tZSBhIGhlbHBmdWwgdG9vbCBmb3IgdmlzdWFsaXppbmcgdGhlIGludGVyc2VjdGlvbiBiZXR3ZWVuIGNvbXBvbmVudHMgd2l0aCBkYXRhc2V0cy4gQWxvbmcgd2l0aCB0aGUgcHVibGljYXRpb24gY2FtZSBhIHBhY2thZ2UgZm9yIHdvcmtpbmcgd2l0aCB0aGVzZSBwbG90cy4gS25vd24gYXMgdGhlIFtgVXBTZXRSYCBwYWNrYWdlXShodHRwczovL2dpdGh1Yi5jb20vaG1zLWRibWkvVXBTZXRSKSwgdGhlcmUgaGF2ZSBzaW5jZSBiZWVuIG1vcmUgcHJvamVjdHMgaW1wbGVtZW50aW5nIHRoaXMga2luZCBvZiB2aXN1YWxpemF0aW9uLiBXaGlsZSBgVXBTZXRSYCBoYXMgbm90IGJlZW4gdXBkYXRlZCBpbiBuZWFybHkgMyB5ZWFycywgYSBtb3JlIGFjdGl2ZSBwYWNrYWdlIGtub3duIGFzIFtgQ29tcGxleFVwc2V0YCBpcyBhdmFpbGFibGVdKGh0dHBzOi8va3Jhc3Nvd3NraS5naXRodWIuaW8vY29tcGxleC11cHNldC9pbmRleC5odG1sKS4NCg0KVG8gc29tZSBleHRlbnQsIHRoZSBgQ29tcGxleFVwc2V0YCBoYXMgZXh0ZW5zaWJpbGl0eSB3aXRoIGBnZ3Bsb3RgLCBhbGxvd2luZyB5b3UgdG8gdXNlIHNvbWV3aGF0IGZhbWlsaWFyIHN5bnRheCB0byBtb2RpZnkgdGhlc2UgcGxvdHMuIEFkZCB0byB0aGlzIHRoZSBhYmlsaXR5IHRvIHN0YWNrIG9yIGluY2x1ZGUgYWRkaXRpb25hbCB2aXN1YWxpemF0aW9ucyBvZiB5b3VyIGRhdGFzZXRzIGRpc3RyaWJ1dGlvbnMgb3Igb3RoZXIgY2hhcmFjdGVyaXN0aWNzLCBhbmQgdGhpcyBpcyBhIHByZXR0eSBhdHRyYWN0aXZlIHBhY2thZ2UgdG8gd29yayB3aXRoLg0KDQotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCg0KIyMjIDQuMy4yIFdvcmtpbmcgd2l0aCB0aGUgYENvbXBsZXhVcHNldGAgcGFja2FnZSB0byB2aXN1YWxpemUgb3ZlcmxhcHBpbmcgZGF0YXNldHMNCg0KTGV0J3Mgc2VlIGhvdyBVcFNldCBwbG90cyB3b3JrIHByYWN0aWNhbGx5LiBXZSBjYW4gY29tcGFyZSBvdXIgZGF0YSBmcm9tIGBibGFuY29fZGF0YV9sb25nLmRmYCBhbmQgY29tcGFyZSB0aGUgb3ZlcmxhcCBvZiBERSBnZW5lcyAoYWZ0ZXIgZmlsdGVyaW5nIG9yIG5vdCkuIFRvIGRvIHRoaXMgaW4gYSBwcmFjdGljYWwgc2Vuc2UsIHdlIGFnYWluIG5lZWQgdG8gY29udmVydCBvdXIgdmFsdWVzIHRvIGEgYm9vbGVhbiByZXByZXNlbnRhdGlvbiBhZnRlciBzb21lIG1vcmUgZGF0YSB3cmFuZ2xpbmcuIE91ciBkYXRhIHdyYW5nbGluZyBzdGVwcyB3aWxsIGluY2x1ZGU6DQoNCjEuICBHZW5lcmF0aW5nIGEgc2hvcnQtbGlzdCBvZiBnZW5lcyB3aXRoIGEgaGlnaCBsb2d+Mn4gZm9sZC1jaGFuZ2UgYW5kIGxvdyBwLXZhbHVlLg0KMi4gIEZpbHRlcmluZyBvdXIgZGF0YSB0byBpbmNsdWRlIG9ubHkgdGhvc2UgZ2VuZXMuDQozLiAgQ29udmVydGluZyBvdXIgZGF0YSB0byBhIHdpZGUgZm9ybWF0IHdoZXJlIGVhY2ggY29sdW1uIHJlcHJlc2VudHMgdGhlIGluY2x1c2lvbiBzdGF0dXMgb2YgYSBnZW5lIChvYnNlcnZhdGlvbikgZm9yIGEgc3BlY2lmaWMgZXhwZXJpbWVudC4NCg0KTGV0J3MgbG9vayBhdCBvdXIgZGF0YSBzdHJ1Y3R1cmUgYWdhaW4uDQoNCmBgYHtyfQ0KaGVhZChibGFuY29fZGF0YV9sb25nLmRmKQ0KYGBgDQoNCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KDQpCZWdpbiBieSBtYWtpbmcgb3VyIHNob3J0LWxpc3Qgb2YgZ2VuZXMgdG8gZmlsdGVyIGJ5Lg0KDQpgYGB7cn0NCiMgZmlsdGVyIGZvciB1cHJlZ3VsYXRlZCBnZW5lcyBpbiBvdXIgU0FSUy1Db1YtMi9BNTQ5IGRhdGEgc2V0IGFuZCBtYWtlIGEgbGlzdCBvZiBnZW5lIG5hbWVzDQpTQVJTX0NvVl8yLmxpc3QgPC0NCiAgYmxhbmNvX2RhdGFfbG9uZy5kZiAlPiUgDQogIA0KICAjIEZpbHRlciBvdXIgZGF0YQ0KICBmaWx0ZXIocGF0aG9nZW4gPT0gIlNBUlMtQ29WLTIiLCANCiAgICAgICAgIGhvc3QgPT0gIkE1NDkiLCANCiAgICAgICAgLi4uKSAlPiUgDQogIA0KICAjIFNlbGVjdCBqdXN0IG91ciBnZW5lIG5hbWVzIHVzaW5nIGEgc2hvcnRoYW5kIGNvbnZlcnNpb24NCiAgcHVsbChHZW5lTmFtZSkNCiAgDQogICMgTGVzcyBjb2RlIHRoYW4gdGhlIGZvbGxvd2luZyBzdGVwczoNCiAgIyBkcGx5cjo6c2VsZWN0KEdlbmVOYW1lKSAlPiUgdW5saXN0KCkgJT4lIGFzLmNoYXJhY3RlcigpDQoNClNBUlNfQ29WXzIubGlzdA0KYGBgDQoNCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KDQpGaWx0ZXIgb3VyIGBibGFuY29fZGF0YV9sb25nLmRmYCBhbmQgcGl2b3QgdG8gYSB3aWRlLWZvcm1hdCBjb250YWluaW5nIG91ciBsb2dpY2FsIHZhbHVlcyBmb3IgaW5jbHVzaW9uLg0KDQpgYGB7cn0NCiMgR2VuZXJhdGUgb3VyIHVwc2V0IGRhdGENCmJsYW5jb191cHNldC5kZiA8LQ0KICBibGFuY29fZGF0YV9sb25nLmRmICU+JSANCiAgDQogICMgRmlsdGVyIG91ciBkYXRhIGxpc3QgdXNpbmcgb3VyIGdlbmVzIG9mIGludGVyZXN0DQogIGZpbHRlcihHZW5lTmFtZSAlaW4lIC4uLikgJT4lIA0KICANCiAgIyBjb252ZXJ0IG91ciBMMkZDIGRhdGEgdG8gYSBsb2dpY2FsIGJhc2VkIG9uIGEgdmFsdWUgb2YgPjINCiAgbXV0YXRlKHVwcmVndWxhdGVkID0gLi4uKSAlPiUgDQogIA0KICAjIFNlbGVjdCBqdXN0IHRoZSBnZW5lIG5hbWVzLCBleHBlcmltZW50cywgYW5kIHRoZSBsb2dpY2FsIHZhcmlhYmxlDQogIGRwbHlyOjpzZWxlY3QoR2VuZU5hbWUsIGV4cGVyaW1lbnQsIC4uLikgJT4lIA0KICANCiAgIyBQaXZvdCBvdXIgZGF0YSB3aWRlIHNvIHRoYXQgZWFjaCBHZW5lTmFtZSBpcyBub3cgYSByb3csIGVhY2ggZXhwZXJpbWVudCBpcyBhIGNvbHVtbg0KICBwaXZvdF93aWRlcihuYW1lc19mcm9tID0gLi4uLCB2YWx1ZXNfZnJvbSA9IC4uLikgJT4lIA0KICANCiAgIyBDb252ZXJ0IHRvIGEgZGF0YWZyYW1lIGZvciB0aGUgQ29tcGxleFVwc2V0IHBhY2thZ2UgKG9yIGl0IHdpbGwgdGhyb3cgYW4gZXJyb3IpDQogIGFzLmRhdGEuZnJhbWUoKQ0KICANCnN0cihibGFuY29fdXBzZXQuZGYpDQpgYGANCg0KLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tDQoNCiMjIyA0LjMuMyBHZW5lcmF0aW5nIG91ciB1cHNldCBkYXRhDQoNCkFzIHlvdSBjYW4gc2VlIGZyb20gb3VyIGRhdGEgdHJhbnNmb3JtYXRpb25zLCB3ZSBub3cgaGF2ZSBhbGwgb2Ygb3VyIGV4cGVyaW1lbnRzIGxpc3RlZCBpbiBjb2x1bW5zLCB3aXRoIGVhY2ggZ2VuZSByZXByZXNlbnRlZCBpbiBlYWNoIHJvdy4gQXMgd2UgbG9vayBkb3duIGVhY2ggY29sdW1uIHdlIHNlZSBhIHZhbHVlIG9mIGBUUlVFYCBvciBgRkFMU0VgIHVzZWQgdG8gZGlmZmVyZW50aWF0ZSBpZiB0aGVyZSB3YXMgb3ZlcmV4cHJlc3Npb24gb2YgdGhhdCBnZW5lIGluIHRoYXQgc3BlY2lmaWMgZXhwZXJpbWVudGFsIGNvbnRleHQuDQoNCklmIHdlIGhhZCBub3QgZmlsdGVyZWQgYmFzZWQgb24gc29tZSBzZXQgb2YgZ2VuZXMsIHdlIHdvdWxkIGhhdmUgYSBkYXRhIGZyYW1lIHdpdGggbW9yZSB0aGFuIDIzSyBjb2x1bW5zISBOb3cgdGhhdCB3ZSBoYXZlIG91ciBnZW5lcyBwcmVzZW50ZWQgaW4gdGhpcyB3YXkgd2UgY2FuIHByb2NlZWQgdG8gZ2VuZXJhdGUgYW4gdXBzZXQgcGxvdC4NCg0KV29ya2luZyB3aXRoIHRoZSBgdXBzZXQoKWAgZnVuY3Rpb24gdG8gYnVpbGQgb3VyIHBsb3QsIHdlIHdpbGwgY29uY2VybiBvdXJzZWx2ZXMgd2l0aCB0aGUgZm9sbG93aW5nIHBhcmFtZXRlcnM6DQoNCi0gICBgZGF0YWA6IGEgZGF0YWZyYW1lIGluY2x1ZGluZyBiaW5hcnkgY29sdW1ucyB0aGF0IHJlcHJlc2VudCBtZW1iZXJzaGlwIGluIGVhY2ggY2xhc3MuDQotICAgYGludGVyc2VjdGA6IGEgbGlzdCBvZiBjb2x1bW4gbmFtZXMgd2hpY2ggd2lsbCBiZSB1c2VkIHRvIGdlbmVyYXRlIHRoZSBpbnRlcnNlY3RpbmcgZGF0YS4NCi0gICBgbmFtZWA6IHRoZSBsYWJlbCBzaG93biBiZWxvdyB0aGUgaW50ZXJzZWN0aW9uIG1hdHJpeC4NCi0gICBgaGVpZ2h0X3JhdGlvYDogcmF0aW8gb2YgdGhlIGludGVyc2VjdGlvbiBtYXRyaXggdG8gdGhlIGludGVyc2VjdGlvbiBoZWlnaHQgKGRlZmF1bHQgPSAwLjUpLg0KLSAgIGB3aWR0aF9yYXRpb2A6IHJhdGlvIG9mIHRoZSBvdmVyYWxsIHNldCBzaXplIHdpZHRoIHRvIGludGVyc2VjdGlvbiBtYXRyaXggd2lkdGggKGRlZmF1bHQgPSAwLjMpLg0KLSAgIGBtaW5fc2l6ZWAgYW5kIGBtYXhfc2l6ZWA6IG1pbmltYWwgYW5kIG1heGltYWwgbnVtYmVyIG9mIG9ic2VydmF0aW9ucyBmb3IgYW4gaW50ZXJzZWN0aW9uIHRvIGJlIGluY2x1ZGVkLg0KLSAgIGBtaW5fZGVncmVlYCBhbmQgYG1heF9kZWdyZWVgOiBtaW5pbWFsIGFuZCBtYXhpbWFsIG51bWJlciBvZiBkZWdyZWVzIGZvciBhbiBpbnRlcnNlY3Rpb24gdG8gYmUgaW5jbHVkZWQuDQotICAgYG5faW50ZXJzZWN0aW9uc2A6IHRoZSBtYXhpbXVtIG51bWJlciBvZiBpbnRlcnNlY3Rpb25zIHRvIGRpc3BsYXkgYmFzZWQgb24gb3VyIGBtaW5fKmAgb3IgYG1heF8qYCBjcml0ZXJpYS4NCi0gICBgdGhlbWVzYDogYSBwYXJhbWV0ZXIgdXNlZCB0byBwYXNzIHRoZW1lIGNoYW5nZXMgdGhyb3VnaCB0aGUgYHVwc2V0X2RlZmF1bHRfdGhlbWVzKClgIG9yIGB1cHNldF9tb2RpZnlfdGhlbWVzKClgIGZ1bmN0aW9ucy4NCg0KYGBge3IsIGZpZy53aWR0aD0xOCwgZmlnLmhlaWdodD0xMH0NCg0KIyBHZW5lcmF0ZSBvdXIgdXBzZXQgcGxvdA0KDQp1cHNldChkYXRhID0gLi4uLCAgICAgICAgICAgICAgICAgICAgICAgIyBQcm92aWRlIG91ciBkYXRhc2V0DQogICAgICBpbnRlcnNlY3QgPSAuLi4sICAjIE5hbWUgdGhlIGNvbHVtbnMgd2Ugd2FudCB0byBhbmFseXNlIA0KICAgICAgbmFtZT0nRXhwZXJpbWVudGFsIGNvbmRpdGlvbicsICAgICAgICAgICAgICAgICMgU2V0IHRoZSBsYWJlbCBiZWxvdyB0aGUgaW50ZXJzZWN0aW9uIG1hdHJpeA0KICAgICAgd2lkdGhfcmF0aW89MC4xLCAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICMgTWFrZSB0aGUgU2V0IHNpemUgd2lkdGggYSBsaXR0bGUgc21hbGxlcg0KICAgICAgbWluX3NpemUgPSAuLi4sICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIyBSZXF1aXJlIHRoZXJlIHRvIGJlIGEgbWluaW11bSBvZiAyIG1lbWJlcnMgdG8gc2hvdyBhbiBpbnRlcnNlY3Rpb24NCiAgICAgIG5faW50ZXJzZWN0aW9ucyA9IC4uLiwgICAgICAgICAgICAgICAgICAgICAgICAgIyBTZXQgdGhlIG1heCBudW1iZXIgb2YgaW50ZXJzZWN0aW9ucyB3ZSB3YW50IHRvIHBsb3QNCiAgICAgIHRoZW1lcyA9IHVwc2V0X2RlZmF1bHRfdGhlbWVzKHRleHQgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDIwKSkgIyBTZXQgdGhlIHBsb3QgdGV4dCBzaXplIHRvIDIwDQogICAgICkNCmBgYA0KDQotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCg0KIyMjIDQuMy40IEludGVycHJldGluZyBvdXIgVXBzZXQgcGxvdA0KDQpGcm9tIG91ciB1cHNldCBwbG90LCB3ZSBjYW4gc2VlIHRoZXJlIGFyZSAzIHJlZ2lvbnMuDQoNCjEuICBUaGUgbGVmdC1oYW5kIGJhcnBsb3QgZGVub3RlcyB0aGUgdG90YWwgbnVtYmVyIG9mIG9ic2VydmF0aW9ucyBpbiBlYWNoIHNldC9jYXRlZ29yeS4gSW4gdGhpcyBjYXNlIHdlJ3JlIHRhbGtpbmcgYWJvdXQgZXhwZXJpbWVudGFsIGNvbmRpdGlvbnMuDQoNCjIuICBUaGUgYm90dG9tIHBsb3QgZ3JhcGhpY2FsbHkgcmVwcmVzZW50cyB0aGUgZGlmZmVyZW50IGNvbWJpbmF0aW9ucyBvZiBlYWNoIGNhdGVnb3J5Lg0KDQozLiAgVGhlIHVwcGVyIGJhcnBsb3QgZGlzcGxheXMgdGhlIG51bWJlciBvZiBvY2N1cmVuY2VzIGZvciB0aGUgY29tYmluYXRpb24gZGlzcGxheWVkIGluIHRoZSBib3R0b20gcGxvdC4NCg0KRnJvbSBvdXIgc2V0IHNpemVzLCBpdCBsb29rcyBsaWtlIGEgTDJGQyB2YWx1ZSBvZiBcPiAyIHdhcyBwZXJoYXBzIHRvbyBzdHJpbmdlbnQgZm9yIHRoZSBJQVYgYW5kIE1FUlMtQ29WIERFIGRhdGEuIFNpbmNlIHdlIGRpZCBmaWx0ZXIgb3VyIGdlbmVzIGluaXRpYWxseSBieSB0aGUgYmVzdCBoaXRzIGluIG91ciBTQVJTLUNPVi0yKEE1NDkpIGRhdGFzZXQsIHdlIGNhbiBzZWUgdGhhdCBpdCBoYXMgdGhlIGhpZ2hlc3QgZnJlcXVlbmN5IGdyb3VwIGF0IDM0IGdlbmVzIHdpdGggb3ZlciBleHByZXNzaW9uIGluIGp1c3QgdGhpcyBzZXQuIFRoZSBuZXh0IGxhcmdlc3Qgb3ZlcmxhcHBpbmcgc2V0IGlzIG9mIDE0IGdlbmVzIGJldHdlZW4gdGhlIFNBUlMtQ29WLTIoQTU0OSkgc2V0IGFuZCB0aGUgUlNWKEE1NDkpIERFIGRhdGEuDQoNCllvdSBjYW4gYWxzbyBzZWUgZnJvbSB0aGUgcGxvdCB0aGF0IHRoZXJlIGFyZSBvbmx5IDkgY29tYmluYXRpb25zIG9mIGRhdGFzZXRzIHdpdGggYW55IHJlYWwgb3ZlcmxhcC4gSGFkIHdlIHNldCBgbWluX3NpemUgPSAxYCBpbnN0ZWFkLCB0aGUgcmVtYWluZGVyIG9mIHRoZSBncm91cGluZyBjb21iaW5hdGlvbnMgd291bGQgYmUgc2VlbiB0byBjb250YWluIGp1c3QgYSBzaW5nbGUgZ2VuZSBpbiBlYWNoLg0KDQpPdmVyYWxsIHRoaXMgY2FuIG1ha2UgZm9yIGEgc2ltcGxpZmllZCBpbnRlcnByZXRhdGlvbiBvZiBvdmVybGFwcGluZyBnZW5lcyB2ZXJzdXMgbW9yZSBjb21wbGljYXRlZCBWZW5uIGRpYWdyYW1zIQ0KDQotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCg0KIyA1LjAuMCBDbGFzcyBzdW1tYXJ5DQoNClRvZGF5IHdlIHRvb2sgYSBsb25nIGxvb2sgYXQgc29tZSBwb3B1bGFyIHZpc3VhbGl6YXRpb25zIGZvciBleHByZXNzaW9uIGRhdGEgc2V0cyBnZW5lcmF0ZWQgYnkgYW4gZXhwZXJpbWVudCBsaWtlIFJOQXNlcS4gT3VyIGFuYWx5c2VzIGZvY3VzZWQgbW9yZSBvbiBoaWdobGlnaHRpbmcgYXNwZWN0cyBvZiB0aGUgZGlmZmVyZW50aWFsIGV4cHJlc3Npb24gaW5mb3JtYXRpb24gaXRzZWxmIGZyb20gYSBiaWctcGljdHVyZSBsb3ctcmVzb2x1dGlvbiB2aWV3IGxpa2Ugdm9sY2FubyBwbG90cyB0byB6b29taW5nIGRvd24gdG8gdGhlIGluZGl2aWR1YWwgZ2VuZSBsZXZlbCB3aXRoIGRvdCBwbG90cy4NCg0KTmV4dCB3ZWVrIHdlJ2xsIGxvb2sgYXQgZXhhbXBsZXMgb2YgImJpZyIgZGF0YSBmcm9tIGFuIGV2ZW4gYnJvYWRlciBzZW5zZSBieSB1c2luZyBkaW1lbnNpb25hbCByZWR1Y3Rpb24gdGVjaG5pcXVlcyBsaWtlIGNsYXNzaWZ5aW5nIG91ciBkYXRhc2V0cyBpbnRvIGdyb3VwcyB3aXRoIHByaW5jaXBsZSBjb21wb25lbnQgYW5hbHlzaXMuDQoNCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KDQojIyA1LjEuMCBXZWVrbHkgYXNzaWdubWVudA0KDQpUaGlzIHdlZWsncyBhc3NpZ25tZW50IHdpbGwgYmUgZm91bmQgdW5kZXIgdGhlIGN1cnJlbnQgbGVjdHVyZSBmb2xkZXIgdW5kZXIgdGhlICJhc3NpZ25tZW50IiBzdWJmb2xkZXIuIEl0IHdpbGwgaW5jbHVkZSBhbiBSIG1hcmtkb3duIG5vdGVib29rIHRoYXQgeW91IHdpbGwgdXNlIHRvIHByb2R1Y2UgdGhlIGNvZGUgYW5kIGFuc3dlcnMgZm9yIHRoaXMgd2VlaydzIGFzc2lnbm1lbnQuIFBsZWFzZSBwcm92aWRlIGFuc3dlcnMgaW4gbWFya2Rvd24gb3IgY29kZSBjZWxscyB0aGF0IGltbWVkaWF0ZWx5IGZvbGxvdyBlYWNoIHF1ZXN0aW9uIHNlY3Rpb24uDQoNCnwgICAgICAgICAgICAgICAgICAgIHwgQXNzaWdubWVudCBicmVha2Rvd24gfCAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8DQp8Oi0tLS0tLS0tLS0tLS0tLS0tOnw6LS0tLS0tLS0tLS0tLS0tLS06fDotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS18DQp8ICAgICAgICBDb2RlICAgICAgICB8ICAgICAgICAgNTAlICAgICAgICAgIHwgXC0gRG9lcyBpdCBmb2xsb3cgYmVzdCBwcmFjdGljZXM/ICAgICAgICAgICAgICAgfA0KfCAgICAgICAgICAgICAgICAgICAgfCAgICAgICAgICAgICAgICAgICAgICB8IFwtIERvZXMgaXQgbWFrZSBnb29kIHVzZSBvZiBhdmFpbGFibGUgcGFja2FnZXM/IHwNCnwgICAgICAgICAgICAgICAgICAgIHwgICAgICAgICAgICAgICAgICAgICAgfCBcLSBXYXMgZGF0YSBwcmVwYXJlZCBwcm9wZXJseSAgICAgICAgICAgICAgICAgICB8DQp8IEFuc3dlcnMgYW5kIE91dHB1dCB8ICAgICAgICAgNTAlICAgICAgICAgIHwgXC0gSXMgb3V0cHV0IGJhc2VkIG9uIHRoZSBjb3JyZWN0IGRhdGFzZXQ/ICAgICAgfA0KfCAgICAgICAgICAgICAgICAgICAgfCAgICAgICAgICAgICAgICAgICAgICB8IFwtIEFyZSBncm91cGluZ3MgYXBwcm9wcmlhdGUgICAgICAgICAgICAgICAgICAgIHwNCnwgICAgICAgICAgICAgICAgICAgIHwgICAgICAgICAgICAgICAgICAgICAgfCBcLSBBcmUgY29ycmVjdCB0aXRsZXMvYXhlcy9sZWdlbmRzIGNvcnJlY3Q/ICAgICB8DQp8ICAgICAgICAgICAgICAgICAgICB8ICAgICAgICAgICAgICAgICAgICAgIHwgXC0gSXMgaW50ZXJwcmV0YXRpb24gb2YgdGhlIGdyYXBocyBjb3JyZWN0PyAgICAgfA0KDQpTaW5jZSBjb2Rpbmcgc3R5bGVzIGFuZCBzb2x1dGlvbnMgY2FuIGRpZmZlciwgc3R1ZGVudHMgYXJlIGVuY291cmFnZWQgdG8gdXNlIGJlc3QgcHJhY3RpY2VzLiBBc3NpZ25tZW50cyAqbWF5KiBiZSByZXdhcmRlZCBmb3Igd2VsbC1jb2RlZCBvciBlbGVnYW50IHNvbHV0aW9ucy4NCg0KWW91IGNhbiBzYXZlIGFuZCBkb3dubG9hZCB0aGUgbWFya2Rvd24gbm90ZWJvb2sgaW4gaXRzIG5hdGl2ZSBmb3JtYXQuIFN1Ym1pdCB0aGlzIGZpbGUgdG8gdGhlIHRoZSBhcHByb3ByaWF0ZSBhc3NpZ25tZW50IHNlY3Rpb24gYnkgMTI6NTkgcG0gb24gdGhlIGRhdGUgb2Ygb3VyIG5leHQgY2xhc3M6IEFwcmlsIDExdGgsIDIwMjQuDQoNCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KDQojIyA1LjIuMCBBY2tub3dsZWRnZW1lbnRzDQoNCioqUmV2aXNpb24gMS4wLjAqKjogY3JlYXRlZCBhbmQgcHJlcGFyZWQgZm9yICoqQ1NCMTAyMUggUyBMRUMwMTQxKiosIDAzLTIwMjEgYnkgQ2FsdmluIE1vaywgUGguRC4gKkJpb2luZm9ybWF0aWNpYW4sIEVkdWNhdGlvbiBhbmQgT3V0cmVhY2gsIENBR0VGLioNCg0KKipSZXZpc2lvbiAxLjAuMSoqOiBlZGl0ZWQgYW5kIHByZXBhcmVkIGZvciAqKkNTQjEwMjBIIFMgTEVDMDE0MSoqLCAwMy0yMDIyIGJ5IENhbHZpbiBNb2ssIFBoLkQuICpCaW9pbmZvcm1hdGljaWFuLCBFZHVjYXRpb24gYW5kIE91dHJlYWNoLCBDQUdFRi4qDQoNCioqUmV2aXNpb24gMS4wLjIqKjogZWRpdGVkIGFuZCBwcmVwYXJlZCBmb3IgKipDU0IxMDIwSCBTIExFQzAxNDEqKiwgMDMtMjAyMyBieSBDYWx2aW4gTW9rLCBQaC5ELiAqQmlvaW5mb3JtYXRpY2lhbiwgRWR1Y2F0aW9uIGFuZCBPdXRyZWFjaCwgQ0FHRUYuKg0KDQoqKlJldmlzaW9uIDIuMC4wKio6IFJldmlzZWQgYW5kIHByZXBhcmVkIGZvciAqKkNTQjEwMjBIIFMgTEVDMDE0MSoqLCAwMy0yMDI0IGJ5IENhbHZpbiBNb2ssIFBoLkQuICpCaW9pbmZvcm1hdGljaWFuLCBFZHVjYXRpb24gYW5kIE91dHJlYWNoLCBDQUdFRi4qDQoNCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQ0KDQojIyA1LjMuMCBSZWZlcmVuY2VzDQoNClNhbmtleSBkaWFncmFtIGRvY3VtZW50YXRpb246IDxodHRwczovL3d3dy5yZG9jdW1lbnRhdGlvbi5vcmcvcGFja2FnZXMvbmV0d29ya0QzL3ZlcnNpb25zLzAuNC90b3BpY3Mvc2Fua2V5TmV0d29yaz4NCg0KYHJpdmVycGxvdGAsIGFub3RoZXIgcGFja2FnZSBmb3IgbWFraW5nIHlvdXIgU2Fua2V5IGRpYWdyYW1zOiA8aHR0cHM6Ly9jcmFuLnItcHJvamVjdC5vcmcvd2ViL3BhY2thZ2VzL3JpdmVycGxvdC9yaXZlcnBsb3QucGRmPg0KDQpNQSBwbG90cyBkaXJlY3RseSBmcm9tIHR3byBzZXRzIG9mIGV4cHJlc3Npb24gZGF0YTogPGh0dHBzOi8vcnBrZ3MuZGF0YW5vdmlhLmNvbS9nZ3B1YnIvcmVmZXJlbmNlL2dnbWFwbG90Lmh0bWw+DQoNCkdlbmVyYXRpbmcgYGdvc2VxYCBkYXRhIGZvciBSTkEgYW5hbHlzaXM6IDxodHRwczovL2Jpb2NvbmR1Y3Rvci5vcmcvcGFja2FnZXMvZGV2ZWwvYmlvYy92aWduZXR0ZXMvZ29zZXEvaW5zdC9kb2MvZ29zZXEucGRmPg0KDQotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0NCg0KIyMgVGhlIENlbnRlciBmb3IgdGhlIEFuYWx5c2lzIG9mIEdlbm9tZSBFdm9sdXRpb24gYW5kIEZ1bmN0aW9uIChDQUdFRikNCg0KVGhlIENlbnRyZSBmb3IgdGhlIEFuYWx5c2lzIG9mIEdlbm9tZSBFdm9sdXRpb24gYW5kIEZ1bmN0aW9uIChDQUdFRikgYXQgdGhlIFVuaXZlcnNpdHkgb2YgVG9yb250byBvZmZlcnMgY29tcHJlaGVuc2l2ZSBleHBlcmltZW50YWwgZGVzaWduLCByZXNlYXJjaCwgYW5kIGFuYWx5c2lzIHNlcnZpY2VzIGluIG1pY3JvYmlvbWUgYW5kIG1ldGFnZW5vbWljIHN0dWRpZXMsIGdlbm9taWNzLCBwcm90ZW9taWNzLCBhbmQgYmlvaW5mb3JtYXRpY3MuDQoNCkZyb20gdGFyZ2V0ZWQgRE5BIGFtcGxpY29uIHNlcXVlbmNpbmcgdG8gdHJhbnNjcmlwdG9tZXMsIHdob2xlIGdlbm9tZXMsIGFuZCBtZXRhZ2Vub21lcywgZnJvbSBwcm90ZWluIGlkZW50aWZpY2F0aW9uIHRvIHBvc3QtdHJhbnNsYXRpb25hbCBtb2RpZmljYXRpb24sIENBR0VGIGhhcyB0aGUgdG9vbHMgYW5kIGtub3dsZWRnZSB0byBzdXBwb3J0IHlvdXIgcmVzZWFyY2guIE91ciBzdGF0ZS1vZi10aGUtYXJ0IGZhY2lsaXR5IGFuZCBleHBlcmllbmNlZCByZXNlYXJjaCBzdGFmZiBwcm92aWRlIGEgYnJvYWQgcmFuZ2Ugb2Ygc2VydmljZXMsIGluY2x1ZGluZyBib3RoIHN0YW5kYXJkIGFuYWx5c2VzIGFuZCB0ZWNobmlxdWVzIGRldmVsb3BlZCBieSBvdXIgdGVhbS4gSW4gcGFydGljdWxhciwgd2UgaGF2ZSBzcGVjaWFsIGV4cGVydGlzZSBpbiBtaWNyb2JpYWwsIHBsYW50LCBhbmQgZW52aXJvbm1lbnRhbCBzeXN0ZW1zLg0KDQpGb3IgbW9yZSBpbmZvcm1hdGlvbiBhYm91dCB1cyBhbmQgdGhlIHNlcnZpY2VzIHdlIG9mZmVyLCBwbGVhc2UgdmlzaXQgPGh0dHBzOi8vd3d3LmNhZ2VmLnV0b3JvbnRvLmNhLz4uDQoNCjo6OiB7YWxpZ249ImNlbnRlciJ9DQo8aW1nIHNyYz0iaHR0cHM6Ly9naXRodWIuY29tL2NhbW9rL0NTQl9Db3Vyc2VfTWF0ZXJpYWxzL2Jsb2IvbWFpbi9BZHZWaXovQ0FHRUZfbmV3LnBuZz9yYXc9dHJ1ZSIgd2lkdGg9IjcwMCIvPg0KOjo6DQo=